常用查询

Unity Shader 使用深度纹理,计算像素的世界坐标

2021-03-17  本文已影响0人  洒一地阳光_217d

Unity Shader系列文章:Unity Shader目录-初级篇

Unity Shader系列文章:Unity Shader目录-中级篇

参考文章:获取深度纹理和法线纹理

我们知道,若已知坐标系中的一个顶点坐标,和它相对于另一个顶点坐标的偏移量,则可以求出另一个顶点坐标。计算像素的世界坐标也是基于这样的思想,我们只需要知道摄像机在世界空间下的位置,以及世界空间下该像素相对于摄像机的偏移量,把它们相加就可以得到该像素的世界坐标。整个过程可以使用下面的代码来表示:

floa worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;

其中,_WorldSpaceCameraPos 是摄像机在世界空间下的位置,这可以由 Unity 的内置变量直接访问得到。而 linearDepth * interpolatedRay 则可以计算得到该像素相对于摄像机的偏移linearDepth 是由深度纹理得到的线性深度值, interpolatedRay 是由顶点着色器输出并插值后得到的射线,它不仅包含了该像素到摄像机的方向,也包含了距离信息。

linearDepth可以通过深度纹理计算:

float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv));

而 interpolatedRay 来源于对近裁剪平面的4个角的某个特定向量的插值,这4个向量包含了它们到摄像机的方向和距离信息,我们可以利用摄像机的近裁剪平面距离、 FOV、横纵比计算而得。下图显示了计算时使用的一些辅助向量。 为了方便计算,我们可以先计算两个向量——toTop 和 toRight,它们是起点位于近裁剪平面中心、分别指向摄像机正上方和正右方的向量。它们的计算公式如下:

halfHeight=Near\times tan(\frac{FOV}{2})
toTop=camera.up\times halfHeight
toRight=camera.right\times halfHeight·aspect

其中, Near 是近裁剪平面的距离, FOV 是竖直方向的视角范围, camera.up、camera.right分别对应了摄像机的正上方和正右方。当得到这两个辅助向量后 ,我们就可以计算4个角相对于摄像机的方向了。我们以左上角为例(见下图中的 TL 点),它的计算公式如下:

TL=camera.forward·Near+toTop-toRight

同理,其他 个角的计算也是类似的:

TR=camera.forward·Near+toTop+toRight
BL=camera.forward·Near-toTop-toRight
BR=camera.forward·Near-toTop+toRight

注意,上面求得的4个向量不仅包含了方向信息,它们的模对应了 个点到摄像机的空间距离。由于我们得到的线性深度值并非是点到摄像机的欧式距离,而是在 z 方向上的距离,因此,我们不能直接使用深度值和4个角的单位方向的乘积来计算它们到摄像机的偏移量,如下图所示。想要把深度值转换成到摄像机的欧式距离也很简单,我们以 TL 点为例,根据相似三角形原理,TL 所在的射线上,像素的深度值和它到摄像机的实际距离的比等于近裁剪平面的距离和 TL向量的模的比,即:

\frac{depth}{dist}=\frac{Near}{|TL|}

由此可得,我们需要的 TL 距离摄像机的欧氏距离 dist:

dist=\frac{|TL|}{Near}\times depth

由于4个点相互对称,因此其他3个向量的模和 TL 相等,即我们可以使用同一个因子和单位向量相乘 ,得到它们对应的向量值:

scale=\frac{|TL|}{|Near|}
Ray_{TL}=\frac{TL}{|TL|}\times scale,Ray_{TR}=\frac{TR}{|TR|}\times scale
Ray_{BL}=\frac{BL}{|BL|}\times scale,Ray_{BR}=\frac{BR}{|BR|}\times scale

计算interpolatedRay 采样得到的深度值并非是点到摄像机的欧式距离

屏幕后处理的原理是使用特定的材质去渲染一个刚好填充整个屏幕的四边形面片。这个四边
形面片的 个顶点就对应了近裁剪平面的 个角。因此,我们可以把上面的计算结果传递给顶点
着色器,顶点着色器根据当前的位置选择它所对应的向量 然后再将其输出,经插值后传递给片
元着色器得到 interpolatedRay,然后就可以根据之前的公式计算该像素在世界空
间下的位置:

floa worldPos = _WorldSpaceCameraPos + linearDepth * interpolatedRay;

上一篇下一篇

猜你喜欢

热点阅读