Position From Depth(2)

来源:互联网 发布:mac qq拼音登录 编辑:程序博客网 时间:2024/06/15 23:08

 

      前面我翻译了一篇从深度图里重建位置信息的文章,我相信很多人还是很难重建成功。这也不能怪大家D,这个东西确实相当棘手,很容易把事情搞砸。所以我打算再从基础开始说起,并解释一些隐含在代码里的数学方法。希望通过一个更广义的,更简单的,也就是不蛋疼的方法去帮助大家摆脱各种困难。不过大家学会之后,还是要做出自己的一份实现。我会把自己的shader粘出来作为增补给大家参考。所有的实现我都会使用左手坐标系去完成,用右手坐标系的自己转换去,不难的。

      让我们从最基础的透视投影开始。每个屏幕上像素都有一个方向向量相关联,你可以计算出这个方向向量:通过使用屏幕空间像素的XY位置信息,去和平截头体的角进行插值,然后减去摄像机的位置,最后归一化。(如果在视点空间,你不必减去摄像机的位置,因为摄像机的位置为0)。如果geometry在这个像素位置光栅化,这就意味着在这个像素的表面必定是这个向量上的某一个点。这个向量的距离到底有多长取决于geometry到摄像机的距离,但方向是保持不变的。这听起来跟前面有人写的光线跟踪算法一样,因为它的确切概念是用于基本光线的:一个远裁剪面或者近裁剪面上的像素,得到从摄像机到这个像素的距离,并检查其相交性。究极含义:如果我们有屏幕空间像素位置、摄像机的位置,我们就可以算出这个三角形表面的位置(如果相机到这个表面存在距离的话)。如下图:

 

 

你可能会想,如果我们把相机到三角形表面的距离信息存储在一个G-Buffer PASS里面,你就很容易重建出位置来。而且结果是完全正确的。这过程的基本步骤:

 

1、在G-Buffer pass的PS里面计算从摄像机到正在着色的表面的距离,并将其写入深度纹理。

2、在light pass的VS里面,计算从摄像机位置到这个顶点的方向向量.(我们把它叫做View Ray).

3、在PS里面,规范化View Ray的向量。

4、采样深度纹理得到从摄像到G-Buffer表面的距离。

5、把采样得到的距离与View ray的向量相乘。

6、最后和摄像机的位置相加。

 

这很简单,性能消耗比较小的,我们只是在视图空间和世界空间计算。在视图空间性能消耗要小一点,并且更简单一点,因为摄影机的位置是(0,0,0),这样你的数学将会更简单。额外要做得事情ViewRay只是规范化的视图空间像素的位置。对于一个full-screen quad,你能得到的视点空间的四方形顶点的位置信息:你可以通过直接映射这些顶点到平截头体的各个角上,或者使用投影矩阵的逆矩阵。你也可以通过使用视图矩阵的逆矩阵把它还原到世界坐标系中去(摄像机世界空间矩阵)。这里是在世界空间做的代码(似乎很多人还是喜欢在世界空间去做,尽管视点空间是有一定优势):

 

 

 

 

说的就像一块蛋糕,够甜,够爽。我觉得这个方法对于大多数人来说效率是足够的,但我没做完,仍可以进一步优化:如果我们坚持在视图空间里面去做。我也希望尽可能的使用硬件深度缓冲,而不是手动存储一个距离值。更深入的研究,你会发现更好的方法,请看下图:

 

      这一次,不再使用规范化方向向量表示View ray,我们推断出ray:其实这条射线就是从摄像机发出最后和远平面相交的所有Ray。如果我们这样做时,这个点(相交的点,Viewray的结束点)会有一个已知深度(远裁剪面的距离)和摄像机的位置,并且方向就是摄像机Looking的方向。在视图空间中,这条view ray的Z分量等同于远裁剪面。假设Z分量是一个已知值,我们就不再需要规范化viewray向量了。相反,我们可以乘一个值,它大小就是摄像机的z轴,最后得到最后重建的位置信息。此时,Z = FarClipDistance,我们通过原始的表面深度和远裁剪面进行比例缩放。换句话说,这个表面视图空间的Z除以远裁剪平面的距离。代码如下:


 

 

 

 

      正如你所看到的,这样更省性能,特别是在做full-screen quad处理的时候。有一件事情必须注意的是,我们存储的规范化深度值,它总是在[0,1]范围。这意味着你可以把它储存在规范化的整形格式(如DXGI_FORMAT_R16_UNORM)采样后不用做任何Rescaling。浮点格式很明显会更好的解决这个问题。

      现在我们讨论另外一个话题,我们采样的硬件深度缓冲,而不是把深度值写入G-Buffer,或者把距离值写入G-Buffer。这样就很剩了,在很多情况下,我们已经有了这块深度缓冲,为何我们不好好利用它呢?一块硬件深度缓冲存储的是Post-Projection后的Z值除以Post-Projection后的W值,W等于视图空间表面position的Z分量,不明白看如下链接:

http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html(可能需要墙一下)

     一块硬件深度缓冲存储的是Post-Projection后的Z值除以Post-Projection后的W值,W等于视图空间表面position的Z分量。这个值不怎么适合我们用,但幸运的是,我们可以从这个使用perspectiveprojection(透视投影)的参数中重建view-space的Z。如果我们这样做,并且想要它是规范化的,可以将它转换成一个规范化的深度值,但这并不是必要的。我们可以计算出Viewray到远裁剪平面,如果我们不是clamp它到的Z = 1的平面,就可以缩放视点空间的Z轴,而不需要一开始就操纵它。请看以下代码:

 

 

     也有可能使用第一种方法的硬件深度缓冲,如果你想工作在任意坐标空间。诀窍就是投影View ray到摄像机的z轴上(又称作摄像机的 forwardvector 或者 lookAt vector),并以此来找到一个适当的缩放的比例。像素着色器代码大概像下面一样:

 

 

好吧,所有的东西都说完了!所有的东西都翻译完了,后面我还会翻译一篇关于深度缓冲的:

http://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/

 

此篇文章翻译得有点差,如果要看原文的:


 

http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/

 


 

 

 

 

原创粉丝点击