投影矩阵:深度映射到什么?

Projection matrices: What should depth map to?

我 运行 在尝试为 Vulkan 构建投影矩阵时陷入矛盾,并且尚未找到投影矩阵应如何将 Z 从输入向量映射到输出的解释。映射 x 和 y 很简单。我的理解是 OpenGL 投影矩阵应该将近视锥体平面映射到 -1,将远视锥平面映射到 +1。 Vulkan 分别为 0 和 +1。映射应该是对数的,允许在近场中有更高的精度。

以下示例使用 near (n) = 1,far (f) = 100。 这是使用我根据 Vulkan 规范构建的矩阵的 z 映射图。它会在渲染中产生错误,但会产生正确的结果,据我所知:

lambda z: (f / (f-n) * z - f*n/(f-n)) / z

我在网上找到的最常见的 OpenGL 投影图,应该从 -1 映射到 +1:

lambda z: ((-f+n)/(f-n)*z - 2*f*n/(f-n))/-z

这是从我使用的库生成的,用于 OpenGL(Rust 中的 cgmath):

我无法构建合适的 Vulkan 投影矩阵(我通过 Google 找到了 none),除非我了解 z 应该映射到什么。我怀疑这是由于着色器完成的隐式校正 post 投影矩阵实际上映射到我列出的范围,但如果是这样,我不知道通过投影垫输入什么范围。

The mapping should be logarithmic, allowing greater precision in the near field.

实际上,如果您不采取任何技巧,映射将是双曲线,而不是对数。关于双曲线映射的关键点是您实际上可以在屏幕中对其进行线性插值 space(当您想要进行一些 Z 缓冲区优化时,这是一个非常好的 属性,例如 Hierarchical Z)。

A plot of the most common OpenGL projection I've found online, which should map from -1 to +1:

lambda z: ((-f+n)/(f-n)*z - 2*f*n/(f-n))/-z 

没有。你第一期符号错误,应该是

(-(f+n)/(f-n)*z - 2*f*n/(f-n))/-z 

因此,你的情节是错误的。使用更正后的公式,您将获得类似于 cgmath rust 库的绘图。

但重要的一点是:你在策划错误的事情! 请注意该公式中分母上的 -z ?经典的 GL 惯例一直是使用 右手 眼 space,但是 左手 window space.结果,经典的 GL 投影矩阵沿 -z 方向投影。不过,参数 nf 仍然作为沿观察方向的距离给出。这意味着,实际的剪辑平面将位于眼睛 space 中的 z_eye = -nz_eye=-f 处。你在图表中绘制的是相机 后面 的范围,你会看到夸张的第二个分支,通常被剪掉的那个,它将映射到相机的外部[-1,1]间隔。

如果绘制 n=5 和 f=100 的映射,您将得到:

请注意,OpenGL 投影到 -z 方向是纯粹的约定,它不受任何强制,因此您也可以使用 +z 投影矩阵。

I'm unable to build a proper Vulkan projection matrix (Of which I've found none via Google) unless I understand what z should map to. Here's a plot of z mapping using a matrix I constructed to the Vulkan spec. It produces errors in rendering, but produces the correct result as I understand it:

lambda z: (f / (f-n) * z - f*n/(f-n)) / z 

不确定您看到的是什么错误,但是如果您

映射是正确的
  1. 采用 vulkan 的默认 [0,1] z 剪辑约定,并且
  2. 想要在眼睛 space 中沿着 +z 的投影方向。

顺便说一句。 vulkans [0,1] 剪辑约定还可以在使用浮点深度附件时更好地使用精度:通过反转映射,使近平面映射到 1,远平面映射到 0,您可以提高精度相当。查看 this nvidia devblog article 了解更多详情。