在URP中实现高度雾和远景雾
使用RenderFeature分别实现了透视相机和正交相机的高度雾和远景雾
如图:
透视相机


正交相机


如何RenderFeature在渲染管线中插入一个Pass用于渲染雾
1. 使用CommondBuffer.DrawMesh 在RenderPassEvent.AfterRenderingSkybox这个事件之后画一个全屏幕大小的Mesh片
这里将投射矩阵重置,方便DrawMesh的坐标计算,绘制完成之后再将摄像机的投射矩阵设置回来。
cmd.SetViewProjectionMatrices(Matrix4x4.identity, Matrix4x4.identity);
cmd.SetViewport(camera.pixelRect);
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_BlitMaterial); cmd.SetViewProjectionMatrices(camera.worldToCameraMatrix, camera.projectionMatrix);
2. 这个Mesh片就是叠上去的一层雾,具体什么地方有雾以及雾的浓度这里就需要开启DepthTexture拿到深度值重建世界坐标计算而得,因为画全屏Mesh的时候我们改变了投射矩阵,所以重建世界坐标我们需要额外从C#传一些参数到shader
_CameraForward: 摄像机方向_ClipToWorldMatrix: 裁剪坐标系到世界坐标系的矩阵_CameraWorldPos: 摄像机世界坐标系位置
3. 如何基于Depth重建世界坐标,这个有很多的实现方式,比如:
4. 通过对这些方式的学习有了下面的方案:
- 从屏幕Mesh面片UV重构NDC坐标,并结合传入的
_ClipToWorldMatrix可以得到摄像机远平面(Far Plane)上的世界坐标, 记为farPlaneWS
这里我写了一个TestCameraPlane.shader 来验证得到下图


然后通过
farPlaneWS - _CameraWorldPos获得从相机到远平面上的点的世界坐标系向量,这里需要注意的是:-
- 这个向量只用于透视相机情况下的世界坐标重建,正交相机用不到这个值,如果使用环境只有正交相机,那就可以将这个值相关的代码删除提高性能
-
- 这个向量不是单位向量,还包含了长度,就是相机到对应远平面点的距离
-
- 为了提高性能,这个向量放在了顶点着色器计算,其他面片上 非顶点的点的向量会被插值器自动处理
-
最后进入片元着色器中进行对应场景内模型的点世界坐标的重建,这里要分2种情况:
-
正交相机情况:
使用远平面的世界坐标当作起始点,朝
_CameraForward反方向运动一定距离z 就是对应点的世界坐标了,很明显,这里的z 就需要使用Depth获得, 具体代码如下:float z = - ( far - near ) * depth; // 这里直接z取反来朝`_CameraForward`反方向 posWS = farPlaneWS + z * _CameraForward;
-
- 透视相机的情况:
使用URP内置的方法
Linear01Depth()获取线性深度,它是从CameraPos作为起始点,FarPlane 作为终点。结合上文提到的距离很容易得到世界坐标的值,具体如下:float z = Linear01Depth(depth, _ZBufferParams); posWS = _CameraWorldPos.xyz + z * output.direction;
-
获取到世界坐标之后就可以结合雾的具体参数生成了,这里就不具体展开。
需要特别注意的是深度在不同平台的处理:
具体可以参考Unity官方文档中关于不同平台Shader的处理的说明:https://docs.unity.cn/2021.1/Documentation/Manual/SL-PlatformDifferences.html#5