Unity Shader 深度与法线纹理

Unity Shader 深度与法线纹理:运动模糊,全局雾效和边缘检测

获取深度和法线纹理

原理:

法线纹理实际上是一张渲染纹理,只不过里面存储的像素值不是颜色值,而是一个高精度的深度值。由于被储存在一张纹理中,深度纹理里的深度值范围是[0,1],而且通常是非线性分布的。

这些深度值来自于顶点变换后得到的归一化的设备坐标(Normalized Device Coordinates,NDC)。

一个模型要绘制到屏幕上,需要把它的顶点从模型空间变换到齐次裁剪空间下,这是通过在顶点着色器中乘以MVP变换矩阵得到的;在变换的最后一步,我们需要使用一个投影矩阵来变换顶点。如果是透视投影类型的摄像机,这个投影矩阵就是非线性的。

在得到NDC后,深度纹理中的像素值可以很方便的计算得到了,这些深度值对应了NDC中顶点坐标的z分量的值,由于NDC中z分量的范围在[-1,1],为了使其可以存到深度纹理中,要进行一次映射:d = 0.5*z(ndc) + 0.5

由于法线向量是个几何工具,而纹理通常只用于储存颜色信息,用纹理储存法线向量不是非常直接。如果你想一想,就会知道纹理中的颜色向量用r、g、b元素代表一个3D向量。类似的我们也可以将法线向量的x、y、z元素储存到纹理中,代替颜色的r、g、b元素。法线向量的范围在-1到1之间,所以我们先要将其映射到0到1的范围。

这会是一种偏蓝色调的纹理(你在网上找到的几乎所有法线贴图都是这样的)。这是因为所有法线的指向都偏向z轴(0, 0, 1)这是一种偏蓝的颜色。法线向量从z轴方向也向其他方向轻微偏移,颜色也就发生了轻微变化,这样看起来便有了一种深度。例如,你可以看到在每个砖块的顶部,颜色倾向于偏绿,这是因为砖块的顶部的法线偏向于指向正y轴方向(0, 1, 0),这样它就是绿色的了。

Unity如何得到深度纹理:

深度纹理可以直接来自于真正的深度缓存,也可以是由一个单独的Pass渲染而得,这取决于使用的渲染路径与硬件。

当使用延迟渲染路径时,深度纹理可以访问到,因为延迟渲染会把这些信息渲染到G-buffer中。

无法直接获取深度缓存时,深度和法线纹理是通过一个单独的Pass渲染而得的:Unity会使用着色器替换技术选择那些渲染类型为Opaque的物体,判断它们使用的渲染队列是否小于等于2500(Background,Geometry和AlphaTest),如果满足条件,就把它渲染到深度和法线纹理中。

深度纹理的属性:

深度纹理的精度通常为24位或16位,这取决于深度缓存的精度。如果生成一张深度+法线纹理,Unity会创建一张与屏幕分辨率相同、精度为32位的纹理,其中观察空间下的法线信息会被编码进纹理的R和G通道,而深度信息会被编码进B和A通道。

如何获取深度纹理:

设置摄像机模式:camera.depthTextureMode = DepthTextureMode.Depth;

如果想获取深度+法线纹理:camera.depthTextureMode = DepthTextureMode.DepthNormals;

使用深度纹理模拟运动模糊:

全局雾效:

使用深度纹理进行边缘检测:


参考:

  1. learnopengl-cn-法线贴图