从像素到 NDC 的转换

2024-01-11

假设我的屏幕是(800 * 600)并且我使用以下顶点位置绘制了一个四边形(2D)三角形_条带(在国家数据中心):

float[] vertices = {-0.2f,0.2f,-0.2f,-0.2f,0.2f,0.2f,0.2f,-0.2f};

我以这种方式设置我的变换矩阵:

Vector2f position = new Vector2f(0,0);
Vector2f size = new Vector2f(1.0f,1.0f);

Matrix4f tranMatrix = new Matrix4f();
tranMatrix.setIdentity();
Matrix4f.translate(position, tranMatrix, tranMatrix);
Matrix4f.scale(new Vector3f(size.x, size.y, 1f), tranMatrix, tranMatrix);

还有我的顶点着色器:

#version 150 core

in vec2 in_Position;

uniform mat4 transMatrix;

void main(void) {

gl_Position = transMatrix * vec4(in_Position,0,1.0);

}

我的问题是,我应该使用哪个公式来修改我的四边形坐标(以像素为单位)的变换?

例如 :

  • 设置比例: (50 像素, 50 像素) =>Vector2f(width,height)

  • 设置位置:(100 像素,100 像素)=>Vector2f(x,y)

为了更好地理解,我将创建一个函数将像素数据转换为 NDC,以便将它们发送到顶点着色器旁边。有人建议我使用正交投影,但我不知道如何正确创建它,正如您在我的顶点着色器中看到的那样,我不使用任何投影矩阵。

这是一个与我类似但不太清楚的主题 -转换到NDC,计算并转换回世界空间 https://stackoverflow.com/q/33897004/6722667


EDIT:

我按照公式创建了正交投影矩阵,但是 似乎什么也没有出现,我是这样进行的:

public static Matrix4f glOrtho(float left, float right, float bottom, float top, float near, float far){
    
    final Matrix4f matrix = new Matrix4f();
    matrix.setIdentity();

    matrix.m00 = 2.0f / (right - left);
    matrix.m01 = 0;
    matrix.m02 = 0;
    matrix.m03 = 0;
    
    matrix.m10 = 0;
    matrix.m11 = 2.0f / (top - bottom);
    matrix.m12 = 0;
    matrix.m13 = 0;
    
    matrix.m20 = 0;
    matrix.m21 = 0;
    matrix.m22 = -2.0f / (far - near);
    matrix.m23 = 0;
    
    matrix.m30 = -(right+left)/(right-left);
    matrix.m31 = -(top+bottom)/(top-bottom);
    matrix.m32 = -(far+near)/(far-near);
    matrix.m33 = 1;

    return matrix;
}

然后我将矩阵包含在顶点着色器中

#version 140

in vec2 position;

uniform mat4 projMatrix;

void main(void){

gl_Position = projMatrix * vec4(position,0.0,1.0);

}

我错过了什么 ?


新答案

经过评论的澄清,所提出的问题可以总结为:

如何有效地转换四边形的像素以在 GUI 中使用?

正如原始问题中提到的,最简单的方法是使用正交投影。什么是正交投影?

一种投影方法,其中使用平行线描绘物体或映射表面,将其形状投影到平面上。

在实践中,您可能会将其视为 2D 投影。距离不起作用,OpenGL 坐标映射到像素坐标。看到这个答案 https://stackoverflow.com/a/2602693/735425了解更多信息。

通过使用正交投影而不是透视投影,您可以开始考虑以像素为单位的所有变换。

而不是将四边形定义为(25 x 25)世界尺寸单位,它是(25 x 25)维度中的像素。

或者代替翻译50沿世界 x 轴的世界单位,您可以翻译为50沿屏幕 x 轴(右侧)的像素。

那么如何创建正交投影呢?

首先,它们通常使用以下参数定义:

  • left- 左侧垂直裁剪平面的 X 坐标
  • right- 右侧垂直剪裁平面的 X 坐标
  • bottom- 底部水平裁剪平面的 Y 坐标
  • top- 顶部水平裁剪平面的 Y 坐标
  • near- 近深度剪裁平面
  • far- 远深度剪裁平面

请记住,所有单位均以像素为单位。典型的正交投影定义为:

glOrtho(0.0, windowWidth, windowHeight, 0.0f, 0.0f, 1.0f);

假设您不(或不能)使用glOrtho https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml(你有自己的Matrix类或其他原因),那么您必须自己计算正交投影矩阵。

正交矩阵定义为:

2/(r-l)       0           0       -(r+l)/(r-l)
   0       2/(t-b)        0       -(t+b)/(t-b)
   0          0       -2/(f-n)    -(f+n)/(f-n)
   0          0           0            1

Source A https://en.wikipedia.org/wiki/Orthographic_projection, Source B https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/orthographic-projection-matrix

此时,我建议使用预制的数学库,除非您决定使用自己的数学库。我在实践中看到的最常见的错误来源之一是与矩阵相关的,你花在调试矩阵上的时间越少,你就有越多的时间专注于其他更有趣的事情。

GLM http://glm.g-truc.net/0.9.8/index.html是一个广泛使用且受人尊敬的库,旨在对 GLSL 功能进行建模。 GLM 实施glOrtho看得到here https://github.com/g-truc/glm/blob/master/glm/gtc/matrix_transform.inl在线100.

如何使用正交投影?

正交投影通常用于在 3D 场景之上渲染 GUI。通过使用以下模式可以很容易地完成此操作:

  1. 清除缓冲区
  2. 应用透视投影矩阵
  3. 渲染您的 3D 对象
  4. 应用正交投影矩阵
  5. 渲染 2D/GUI 对象
  6. 交换缓冲区

旧答案

请注意,这回答了错误的问题。它假设问题归结为“如何从屏幕空间转换为 NDC 空间?”。留下它是为了防止有人搜索这个问题并寻找答案。

目标是将屏幕空间转换为 NDC 空间。因此,我们首先定义这些空间是什么,然后我们可以创建一个转换。

标准化设备坐标

NDC 空间只是对剪辑空间中的顶点执行透视除法的结果。

clip.xyz /= clip.w

Where clip是剪辑空间中的坐标。

其作用是将所有未裁剪的顶点放入一个单位立方体中(在[-1, 1]在所有轴上),屏幕中心位于(0, 0, 0)。任何被剪裁的顶点(位于视锥体之外)都不在该单位立方体内,并且会被 GPU 丢弃。

在 OpenGL 中,此步骤作为以下部分自动完成原始装配 https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview#Clipping(D3D11 在光栅化阶段 https://msdn.microsoft.com/en-us/library/windows/desktop/bb205125(v=vs.85).aspx).

屏幕坐标

屏幕坐标只需将标准化坐标扩展到视口的范围即可计算。

screen.x = ((view.w * 0.5) * ndc.x) + ((w * 0.5) + view.x)
screen.y = ((view.h * 0.5) * ndc.y) + ((h * 0.5) + view.y)
screen.z = (((view.f - view.n) * 0.5) * ndc.z) + ((view.f + view.n) * 0.5)

Where,

  • screen是屏幕空间中的坐标
  • ndc是标准化空间中的坐标
  • view.x是视口 x 原点
  • view.y是视口 y 原点
  • view.w是视口宽度
  • view.h是视口高度
  • view.f视口是否远
  • view.n视口是否靠近

从屏幕转换为 NDC

由于我们上面有从 NDC 到 Screen 的转换,所以很容易计算逆向 https://www.khronos.org/opengl/wiki/Compute_eye_space_from_window_space#From_window_to_ndc.

ndc.x = ((2.0 * screen.x) - (2.0 * x)) / w) - 1.0
ndc.y = ((2.0 * screen.y) - (2.0 * y)) / h) - 1.0
ndc.z = ((2.0 * screen.z) - f - n) / (f - n)) - 1.0

Example:

viewport (w, h, n, f) = (800, 600, 1, 1000)

screen.xyz = (400, 300, 200)
ndc.xyz = (0.0, 0.0, -0.599)

screen.xyz = (575, 100, 1)
ndc.xyz = (0.4375, -0.666, -0.998)

进一步阅读

有关所有变换空间的更多信息,请阅读OpenGL 转换 http://www.songho.ca/opengl/gl_transform.html.

编辑评论

在对原始问题的评论中,Bo 将屏幕空间原点指定为左上角。

对于 OpenGL,视口原点(以及屏幕空间原点)位于左下角。看GL视口 https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glViewport.xml.

如果您的像素坐标确实是左上角原点,那么在转换时需要考虑到这一点screen.y to ndc.y.

ndc.y = 1.0 - ((2.0 * screen.y) - (2.0 * y)) / h)

如果您要将屏幕/gui 上的鼠标单击坐标转换为 NDC 空间(作为完全转换为世界空间的一部分),则需要这样做。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从像素到 NDC 的转换 的相关文章

  • Java new Date() 打印

    刚刚学习 Java 我知道这可能听起来很愚蠢 但我不得不问 System out print new Date 我知道参数中的任何内容都会转换为字符串 最终值是 new Date 返回对 Date 对象的引用 那么它是如何打印这个的呢 Mo
  • Java Swing:从 JOptionPane 获取文本值

    我想创建一个用于 POS 系统的新窗口 用户输入的是客户拥有的金额 并且窗口必须显示兑换金额 我是新来的JOptionPane功能 我一直在使用JAVAFX并且它是不同的 这是我的代码 public static void main Str
  • Spring Batch 多线程 - 如何使每个线程读取唯一的记录?

    这个问题在很多论坛上都被问过很多次了 但我没有看到适合我的答案 我正在尝试在我的 Spring Batch 实现中实现多线程步骤 有一个包含 100k 条记录的临时表 想要在 10 个线程中处理它 每个线程的提交间隔为 300 因此在任何时
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • Java JDBC:更改表

    我希望对此表进行以下修改 添加 状态列 varchar 20 日期列 时间戳 我不确定该怎么做 String createTable Create table aircraft aircraftNumber int airLineCompa
  • 给定两个 SSH2 密钥,我如何检查它们是否属于 Java 中的同一密钥对?

    我正在尝试找到一种方法来验证两个 SSH2 密钥 一个私有密钥和一个公共密钥 是否属于同一密钥对 我用过JSch http www jcraft com jsch 用于加载和解析私钥 更新 可以显示如何从私钥 SSH2 RSA 重新生成公钥
  • Final字段的线程安全

    假设我有一个 JavaBeanUser这是从另一个线程更新的 如下所示 public class A private final User user public A User user this user user public void
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • 我可以使用 HSQLDB 进行 junit 测试克隆 mySQL 数据库吗

    我正在开发一个 spring webflow 项目 我想我可以使用 HSQLDB 而不是 mysql 进行 junit 测试吗 如何将我的 mysql 数据库克隆到 HSQLDB 如果您使用 spring 3 1 或更高版本 您可以使用 s
  • 如何为俚语和表情符号构建正则表达式 (regex)

    我需要构建一个正则表达式来匹配俚语 即 lol lmao imo 等 和表情符号 即 P 等 我按照以下示例进行操作http www coderanch com t 497238 java java Regular Expression D
  • 从 127.0.0.1 到 2130706433,然后再返回

    使用标准 Java 库 从 IPV4 地址的点分字符串表示形式获取的最快方法是什么 127 0 0 1 到等效的整数表示 2130706433 相应地 反转所述操作的最快方法是什么 从整数开始2130706433到字符串表示形式 127 0
  • Java按日期升序对列表对象进行排序[重复]

    这个问题在这里已经有答案了 我想按一个参数对对象列表进行排序 其日期格式为 YYYY MM DD HH mm 按升序排列 我找不到正确的解决方案 在 python 中使用 lambda 很容易对其进行排序 但在 Java 中我遇到了问题 f
  • getResourceAsStream() 可以找到 jar 文件之外的文件吗?

    我正在开发一个应用程序 该应用程序使用一个加载配置文件的库 InputStream in getClass getResourceAsStream resource 然后我的应用程序打包在一个 jar文件 如果resource是在里面 ja
  • 总是使用 Final?

    我读过 将某些东西做成最终的 然后在循环中使用它会带来更好的性能 但这对一切都有好处吗 我有很多地方没有循环 但我将 Final 添加到局部变量中 它会使速度变慢还是仍然很好 还有一些地方我有一个全局变量final 例如android Pa
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • 如何在桌面浏览器上使用 webdriver 移动网络

    我正在使用 selenium webdriver 进行 AUT 被测应用程序 的功能测试自动化 AUT 是响应式网络 我几乎完成了桌面浏览器的不同测试用例 现在 相同的测试用例也适用于移动浏览器 因为可以从移动浏览器访问 AUT 由于它是响
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 如何修复 JNLP 应用程序中的“缺少代码库、权限和应用程序名称清单属性”?

    随着最近的 Java 更新 许多人都遇到了缺少 Java Web Start 应用程序的问题Codebase Permissions and Application name体现属性 尽管有资源可以帮助您完成此任务 但我找不到任何资源综合的
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview

随机推荐