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

时间:2017-02-07 19:05:28

标签: graphics perspective depth-buffer

我基于光栅化和深度缓冲来实现我自己的渲染器。正如您在下一张图片中看到的那样正常工作

Sample image of a rendered box of dimensions 1000x1000x10

然而,非常错误!即使盒子看起来像一个立方体,它的尺寸也是1000x1000x10。 "缩短"太高了。如果我将尺寸更改为1000x1000x1000,则框会变为无限:

Sample image of a rendered box of dimensions 1000x1000x1000

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

  const Point3D point_camera = {
     point_world.x * m_left.x + point_world.y * m_up.x + point_world.z * m_forward.x - m_position.x,
     point_world.x * m_left.y + point_world.y * m_up.y + point_world.z * m_forward.y - m_position.y,
     point_world.x * m_left.z + point_world.y * m_up.z + point_world.z * m_forward.z - m_position.z
  };

然后我应用透视划分将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与此有关?如果有人可以帮助我或让我对透视投影背后的理论有一个很好的解释,我将非常感激。

所有代码都在github上。相关课程为PerspectiveCameraForwardRasterizer

1 个答案:

答案 0 :(得分:0)

因此,如果您想使用4x4矩阵转换3维向量或点,您可能已经知道,您需要使用另一个组件(通常称为 w )来扩充它。为了正确转换, w 通常选择为1 (x,y,z)变为(x,y,z,1)< /强>

让我们假设您的点已经转换为视图空间,因此它位于相机的“前方”。您要执行的下一个和最后一个转换是实际投影。如果将向量乘以this矩阵(与Datenwolfs略有不同),您将得到一个与此类似的结果列向量

Vector multiplication against perspective matrix

我应该提一下,在这个向量中,我省略了 x z 的缩放比例,只是为了让事情更加清晰。

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

enter image description here

如果为 z 插入一些[10,100]范围内的不同数字,那么您会发现这确实只是将 z 标准化。

投影的魔力发生在一个看不见的步骤中,由 x y' z'除以 w'将实际的效果应用于屏幕空间中的 x' y',但也会将 z'重新规范化为当它远离观众时,它的价值看起来像这样。 enter image description here

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

我想要注意的最后一件事是列向量中 w'的负值。你可能会想到自己,“为什么他们会这样做呢?这不会垂直和水平地翻转所有东西吗?”而你是对的,这正是它的作用。我之前已经提到过几次这样的原因。规范视图卷,对于OpenGL,屏幕空间映射看起来像这样。

enter image description here

与其他屏幕坐标系不同,原点位于屏幕的死点。无论屏幕的尺寸如何。除了那个怪癖之外,最正的坐标位于屏幕的右上角,而不是右下角。因此,在许多情况下,您的投影坐标必须翻转,因此 -z w'的值。

我希望这会帮助你!