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

时间:2018-10-19 13:58:25

标签: opengl graphics vulkan

在尝试为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 enter image description here

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

lambda z: ((-f+n)/(f-n)*z - 2*f*n/(f-n))/-z enter image description here

这是我使用的一个库生成的,用于OpenGL(Rust中的cgmath): enter image description here

除非我了解z应当映射到什么,否则我将无法建立适当的Vulkan投影矩阵(通过Google找不到该矩阵)。我怀疑这是由于着色器对投影后的矩阵进行了隐式校正,实际上将其映射到我列出的范围,但是如果是这样,我不知道通过投影垫将什么范围输入到其中。

1 个答案:

答案 0 :(得分:8)

  

映射应为对数映射,以便在近场中获得更高的精度。

实际上,如果您不做任何技巧,则映射将是双曲线的,而不是对数的。关于双曲线映射的关键是您实际上可以在屏幕空间中对其进行线性插值(当您要进行诸如Z分层的Z缓冲区优化时,这是一个非常好的属性。)

  

我在网上找到的最常见的OpenGL投影图,应该从-1映射到+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锈库类似的图。

但是重要的一点是:您正在绘制错误的东西! 注意该公式中分母上的-z吗?经典的GL约定一直是使用右手眼睛空间,但使用左手窗口空间。结果,经典的GL投影矩阵沿-z方向投影。不过,参数nf仍然是沿观看方向的距离。这意味着实际的裁剪平面将位于眼部空间中的z_eye = -nz_eye=-f处。您在图表中绘制的是相机后面的范围,您将看到双曲线的第二个分支,该分支通常会被剪切,并且会映射到{{1 }}时间间隔。

如果绘制n = 5和f = 100的映射,您将得到: plot of hyperbolic Z function

请注意,OpenGL的[-1,1]方向项目是纯约定俗成的,它不受任何约束,因此您也可以使用-z投影矩阵。

  

除非我了解z应当映射到什么,否则我将无法建立适当的Vulkan投影矩阵(通过Google找不到该矩阵)。   这是使用我根据Vulkan规范构造的矩阵进行z映射的图。它会产生渲染错误,但据我了解会产生正确的结果:

+z

不知道您在此过程中遇到什么错误,但是如果您映射正确,那么

  1. 假设vulkan的默认[0,1] z剪辑约定,并且
  2. 想要在眼部空间中沿lambda z: (f / (f-n) * z - f*n/(f-n)) / z 投影。

顺便说一句。 vulkans +z剪辑约定还可以在使用浮点深度附件时更好地使用精度:通过反转映射,以便将近平面映射为1,将远平面映射为0,可以改善精度相当高。请查看this nvidia devblog article,了解更多详细信息。