如何处理 3D 透视投影中的负深度

How to deal with negative depth in 3D perspective projection

背景

这个问题与 this question 3 年前问的非常相似。基本上,我想重新创建一个基本的第一人称图形引擎作为一种学习体验。

因此,举例来说,我们处于 3D space 中,其中 z 代表深度 - xy 映射到 xy 二维坐标 space。如果此坐标系的原点是相机,则 (0, 0, 1) 处的点将直接位于相机前方,而 (0, 0, -1) 处的点将直接位于相机后方。

为该投影添加深度只需要我们将 xy 分量除以 depth(在本例中为 z)。在实践中,这对我来说很有意义,而且似乎有效。

直到...

...深度变为负值。如果深度为负,用深度除以xyxy的符号会改变。然而,我们知道从逻辑上讲,情况不应该如此。

到目前为止我已经尝试了一些东西:

那么,您如何处理透视投影中的负深度?

我对图形还很陌生,所以如果我遗漏了回答这个问题所需的任何信息,请随时提问。我想让这个实现保持不可知,因为我觉得这个问题更倾向于透视投影的理论方面。

编辑

video 指出了我要解决的问题。这是一个很棒的视频,也是启发我开始这个小项目的原因 - 但我只是想知道是否有一种通用的 'agreed-upon' 方法来处理这种特殊情况。

您正在进行点投影,这意味着您在 2D 中的投影点恰好是 3D 对象和 3D 相机之间的线穿过 canvas 的点。对于正深度,该交点位于对象和相机之间。对于负深度,交点超出相机。但它仍然是同一条线,因此交换符号非常有意义。

当然,实际上绘制具有负深度的东西并没有多大意义,因为通常你不会看到相机后面的东西。如果你这样做,那么你有一些非常广角的镜头,所以假设 canvas 作为 space 中的 平面 不再准确,你会必须切换到更复杂的投影来模拟鱼眼镜头和类似镜头。

然而,您可能想要绘制一个三角形或其他几何基元,并且只有一个角的深度为负,而其他角的深度为正。在这种情况下,通常的方法是将对象裁剪到平截头体,更具体地说是将其与平截头体的近平面相交,从而消除所有具有负深度的点。通常您的图形管道可以处理此裁剪。

我会尝试为任何感兴趣的人提供更数学化的答案。

这背后的数学理论叫做projective geometry。您从三维 space 开始,然后将其拆分为等价 classes,其中两个点 ab 是等价的,如果有一个因素 f 所以f*a == b。因此,例如 (4, 4, 4) 将与 (1, 1, 1) 处于相同的 class,而 (3, 6, 9) 将与 (100, 200, 300) 处于相同的 class。从几何上讲,你看通过(0, 0, 0).

的那组直线

如果您从每个等价 class 中选择带有 z == 1 的点,您基本上会得到一个 2D space。这正是“透视投影”。然而,等价的 classes 对于像 (1, 1, 0) 这样的点没有这样的点。所以你实际得到的是 2D space + 一些额外的“无穷远点”。

您可以将这些点想象成一个围绕您的坐标系的圆,但半径无限大。此外,相反的点是相同的,所以从一端出去的东西会环绕并从另一侧返回。这意味着直线实际上只是包含无穷远点的圆。

举个具体的例子。如果你想渲染一条从 (1, 1, 4)(1, 1, -4) 的直线,你首先将它们都归一化为 z == 1(0.25, 0.25, 1)(-0.25, -0.25, 1)。但是现在当你在它们之间画线时,你需要“反过来”,即离开屏幕的一个方向并从另一侧返回。 (你可以跳过“回来”部分,因为它在镜头后面。)

不幸的是,将 (1, 1, -4) 映射到 (inf, inf, 1) 是不够的,因为那样就无法知道直线的斜率。您可以通过使用非常大的数字而不是无穷大来伪造它,或者您可以正确地做到这一点并在整个代码中处理这些特殊情况。