UnityShade入门精要(9·优化屏幕后期处理)
深度和法线纹理
我们还可以利用深度和法线纹理优化屏幕后期处理
深度贴图存储的是NDC(-1——1)map(0——1)后的z深度
如果为摄像机设置一张仅深度纹理,对于延迟渲染路径,unity是通过G缓冲直接得到的,对于前向渲染,通过深度缓冲可以直接得到
如果为摄像机设置一张深度+法线纹理,对于延迟渲染路径,unity是通过G缓冲直接得到的,对于前向渲染,通过一个单独的pass渲染到深度和法线纹理中,RG通道存储深度信息,BA通道存储法线信息
获取深度和法线纹理
将摄像机的depthTextureMode设置仅深度纹理/深度+法线纹理,对于CameraDepthTexture直接采样就可以得到深度/法线信息,利用SAMPLE_DEPTH_TEXTURE宏采样(避免的平台差异),对于_CameraDepthNormalsTexture,需要通过DecodeDepthNormal函数解码,
采样而来的深度值往往是非线性的,可以根据view->clip->NDC->depth的变换关系,用depth表示view,从而获得原始的view值,再将它/far,即可获得线性的深度值
Unity提供了两个内置辅助函数,LinearEyeDepth采样结果转换到视角空间下的深度值,LinearOlDepth会返回一个范围在[O, 1]的线性深度值,_ZBufferParams变量来得到远近裁剪平面的距离
打开Window> Analysis > Frame Debugger帧调试器,如果相机指定了纹理目标,帧调试器就会出现事件,但是只能查看非线性空间的深度值,如果想要查看线性的,需要shader片段着色器输出的颜色为线性深度,在获取到深度值后用内置的LinearOlDepth函数转换
也可以输出从法线纹理采样的法线方向,我们应该将摄像机的远近裁剪范围刚好覆盖住场景,否则如果场景距离较近,几乎为全黑,反之几乎全白
优化运动模糊
我们上节使用混合多张屏幕图像(非每帧渲染多次,根据一个生成其他)取平均值的方式生成运动模糊图像,这次使用速度映射图(存储了每个像素的速度矢量),来决定模糊的方向和大小
生成速度映射图的一种方法:利用深度贴图的深度值,变换(NDC坐标( *2-1) *VP的逆矩阵)到世界坐标, * 上一帧VP得到上一帧的NDC坐标,从而根据两帧的位置差,生成该像素的速度
首先建立一个相机移动脚本,让相机像乒乓球一样来回运动
在cs脚本中,向屏幕后期处理的材质,传入矩阵和逆矩阵等参数
在shader的片段着色器中,会计算出位置差,根据位置差采样物体纹理,获得的颜色值取平均
屏幕雾效
实现雾效有很多种方法,本次使用屏幕后期处理的雾效,本质上利用深度纹理获取像素对应的世界空间位置,生成例如基于高度的雾效
世界坐标:
我们上次获取世界坐标做了矩阵运算,耗费性能,这次我们通过floa七4 worldPos = _WorldSpaceCaeraPos + linearDepth * interpolatedRay,摄像机的世界位置 + 像素的线性深度值 * 视锥体射线(相机到像素方向)
如何计算interpolatedRay?
首先计算一些辅助向量,我们已知摄像机的属性(近裁剪平面距离、 FOY、横纵比),首先计算近平面中心到两侧to Top 和toRight向量,根据它们可以获得四个顶点的向量,TL,TR,BL,BR(之后的像素方向就可以在片段着色器中自动插值了)
这里由于我们计算的TL是像素到相机的距离,如果想要获取实际点到摄像机的距离,需要根据相似三角形原理,求dist
雾的计算:
雾效系数f:作为混合原始颜色和雾的颜色的混合系数:f有很多计算方法:
线性 (Linear)、指数 (Exponential) 以及指数的平方 (Exponential Squared)
实例:
本节使用线性高度雾的计算方式,在cs中,计算四个方向和向材质传入一些参数控制
在shader的vert中,利用纹理坐标确定当前顶点对应的索引,根据索引确定对应的ray方向,在frag中首先将像素转为世界坐标,根据无效计算公式计算此高度受到的线性影响,根据权重lerp混合颜色
优化边缘检测
我们上节实现的后期处理效果都只是使用屏幕图像的颜色实现的,因此比如边缘检测会受到光照等影响,如果仅仅想要获得更加准确的物体边缘信息,需要在深度纹理和法线纹理上进行边缘检测、
这次的原理是利用对角像素的深度和法线差值小于一定阈值,判断是否是边缘,其中卷积核使用Roberts 算子的本质就是计算左上角和右下角的差值,
在cs脚本中向材质传入参数即可
在shader中,每个像素总共4个偏移量,分别比较对角,当深度和法线都相差不超过0.1,则不是边缘,当两次对比都不是边缘,则不是边缘,根据是否是边缘lerp边缘和本身的颜色