Graphics 透视投影错误(不理解理论)

Graphics 透视投影错误(不理解理论),graphics,perspective,depth-buffer,Graphics,Perspective,Depth Buffer,我正在用CPU实现我自己的基于光栅化和深度缓冲的渲染器。正如您在下一张图片中所看到的,它可以正常工作 然而,这里有一些严重的错误!即使盒子看起来像个立方体,它的尺寸也是1000x1000x10。“缩短时间”太高了。如果我将尺寸更改为1000x1000x1000,则长方体将变为无穷大: 这是因为我在透视投影中遗漏了一些东西。当我进行透视投影(3D世界到2D屏幕)时,我应用视图变换(将坐标系放置在相机位置并具有正确的方向)。为了简化,我的相机与世界的方向相同,唯一改变的是位置,即(0,0,-1)

我正在用CPU实现我自己的基于光栅化和深度缓冲的渲染器。正如您在下一张图片中所看到的,它可以正常工作

然而,这里有一些严重的错误!即使盒子看起来像个立方体,它的尺寸也是1000x1000x10。“缩短时间”太高了。如果我将尺寸更改为1000x1000x1000,则长方体将变为无穷大:

这是因为我在透视投影中遗漏了一些东西。当我进行透视投影(3D世界到2D屏幕)时,我应用视图变换(将坐标系放置在相机位置并具有正确的方向)。为了简化,我的相机与世界的方向相同,唯一改变的是位置,即(0,0,-1):

然后我应用透视分割法,将3D世界点的每个组件除以其Z:

  const Point2D point_projected = {
     (point_camera.x * m_near * m_zoom) / point_camera.z,
     (point_camera.y * m_near * m_zoom) / point_camera.z
  };
我觉得我应该把Z乘以某种因子……但我想不出来。也许
w
与此有关?如果有人能帮助我或让我很好地解释透视投影背后的理论,我将非常感激


所有的密码都在我的电脑上。相关的类是透视摄影机和前向光栅化器。因此,如果您想使用4x4矩阵变换三维向量或点,您可能已经知道,您需要使用另一个组件(通常称为w)来增强它。为了适当的变换,w通常被选择为1,使得(x,y,z)变成(x,y,z,1)

假设您的点已转换为视图空间,因此它位于摄影机的“前面”。要执行的下一个也是最后一个转换是实际投影。如果将向量与矩阵相乘(这与Datenwolfs有点不同),则得到的列向量与此类似

我应该提到的是,在这个向量中,我省略了纵横比的xz的缩放,只是为了让事情更清楚一点

您可以看到向量中的第三个元素正在有效地规范化z,如您所述。让我们假设w仍然是1,f是我们的远平面,是100,n我们的近平面是10。有了这个假设,第三个元素就变成了

如果您为[10100]范围内的z插入几个不同的数字,那么您将看到这实际上只是规范化z

投影的魔力发生在一个看不见的步骤中,x'y'z'除以w',这将实际的透视效果应用于屏幕空间中的x'y',但也会重新规范化z',使其值在点离开查看器时看起来像这样。

作为旁注,图形的形状是有意的。这是为了利用深度缓冲区的精度限制,但这完全是另一种讨论

最后我要注意的是列向量中w'的负值。你可能会想,“他们到底为什么要这样做?这难道不会使所有东西垂直和水平翻转吗?”你是对的,这正是它所做的。原因是我已经提到过好几次了。规范化视图卷,对于OpenGL,其屏幕空间映射如下所示

与其他屏幕坐标系不同,原点位于屏幕的死点。不管屏幕的大小。除此之外,最正的坐标位于屏幕的右上角,而不是右下角。因此,在许多情况下,投影坐标必须翻转,因此-z作为w'的值


我希望这对你有帮助

试着把你相机类的成员m_far换成更大的,可能是2000而不是50。您1000^3的盒子远远超出此范围。通常情况下,如果您使用openGL或其他图形API,那么这种距离的几何体将被剪切到规范视图卷中,因此您不会得到如此奇怪的效果。在我之前图像的本地代码中,m_far被设置为5000,但很好的建议啊,我想我可能已经了解了一些东西:)在这种情况下,看看这个githhub回购协议。它是一个单独的头文件,实现了一系列常用的矩阵、四元数和向量运算。我几乎只在我所有的项目中使用它。作者在那里有一个可靠的透视投影实现。也许检查一下你自己,看看他在做什么。他用一个公式来规范化范围[-1,1]内的z来做透视除法。我已经在我的代码中包含了这个公式,但我仍然无法看到它是否有效,因为我发现了一个必须首先修复的bug。好吧,现在我已经修复了所有东西,它工作得更好了,但我仍然不明白为什么。在透视分水岭中,我不是按
point_camera.z
潜水,而是按
((m_far+m_near)/(m_far-m_near))+(1/深度)*((2*m_near*m_far)/(m_far-m_near))
这个答案帮助我找到了解决方案,但我想做一些笔记。首先,我认为你给出的第一个等式是不正确的,因为在第一个元素中有一个额外的负号。它应该是
(z*f)/(f-n)-w*((f*n)/(f-n))
其次,当替换第一个方程的值时,您缺少一些参数。方程式应为
100z/90-1000/90
。他说,答案是,
  const Point2D point_projected = {
     (point_camera.x * m_near * m_zoom) / point_camera.z,
     (point_camera.y * m_near * m_zoom) / point_camera.z
  };