【发布时间】:2012-01-19 10:42:48
【问题描述】:
我正在使用 OpenGL 制作游戏,其中我在世界空间中有一些对象。我想创建一个函数,我可以在其中获取对象的位置(3D)并将其转换为屏幕的位置(2D)并返回它。
我在以下变量中知道对象、投影矩阵和视图矩阵的 3D 位置:
Matrix projectionMatrix;
Matrix viewMatrix;
Vector3 point3D;
【问题讨论】:
我正在使用 OpenGL 制作游戏,其中我在世界空间中有一些对象。我想创建一个函数,我可以在其中获取对象的位置(3D)并将其转换为屏幕的位置(2D)并返回它。
我在以下变量中知道对象、投影矩阵和视图矩阵的 3D 位置:
Matrix projectionMatrix;
Matrix viewMatrix;
Vector3 point3D;
【问题讨论】:
要进行此转换,您必须首先获取模型空间位置并将它们转换为剪辑空间。这是通过矩阵乘法完成的。我将使用 GLSL 风格的代码来明确我在做什么:
vec4 clipSpacePos = projectionMatrix * (viewMatrix * vec4(point3D, 1.0));
请注意我是如何在乘法之前将您的 3D 向量转换为 4D 向量的。这是必要的,因为矩阵是 4x4,您不能将 4x4 矩阵与 3D 向量相乘。你需要第四个组件。
下一步是将此位置从剪辑空间转换为标准化设备坐标空间(NDC 空间)。 NDC 空间在所有三个轴上的范围 [-1, 1] 上。这是通过将前三个坐标除以第四个坐标来完成的:
vec3 ndcSpacePos = clipSpacePos.xyz / clipSpacePos.w;
显然,如果clipSpacePos.w 为零,则说明您有问题,因此您应该事先检查。如果为零,则表示对象在投影平面内;它的视图空间深度为零。并且这样的顶点会被 OpenGL 自动裁剪掉。
下一步是从这个 [-1, 1] 空间转换为窗口相对坐标。这需要使用您传递给glViewport 的值。前两个参数是窗口左下角的偏移量(vec2 viewOffset),后两个参数是视口区域的宽度/高度(vec2 viewSize)。鉴于这些,窗口空间位置是:
vec2 windowSpacePos = ((ndcSpacePos.xy + 1.0) / 2.0) * viewSize + viewOffset;
这就是你所能做的。请记住:OpenGL 的窗口空间是相对于窗口的左下角,而不是左上角。
【讨论】:
windowSpacePos应该是vec2( ((ndcSpacePos.x + 1.0) / 2.0) * viewSize.x + viewOffset.x, ((1.0 - ndcSpacePos.y) / 2.0) * viewSize.y + viewOffset.y )