在URP中实现高度雾和远景雾

发布时间: 2022年05月21日阅读数: 2

使用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 获得从相机到远平面上的点的世界坐标系向量,这里需要注意的是:

      1. 这个向量只用于透视相机情况下的世界坐标重建,正交相机用不到这个值,如果使用环境只有正交相机,那就可以将这个值相关的代码删除提高性能
      1. 这个向量不是单位向量,还包含了长度,就是相机到对应远平面点的距离
      1. 为了提高性能,这个向量放在了顶点着色器计算,其他面片上 非顶点的点的向量会被插值器自动处理
  • 最后进入片元着色器中进行对应场景内模型的点世界坐标的重建,这里要分2种情况:

      1. 正交相机情况:

        使用远平面的世界坐标当作起始点,朝_CameraForward反方向运动一定距离z 就是对应点的世界坐标了,很明显,这里的z 就需要使用Depth获得, 具体代码如下:

        float z = - ( far - near ) * depth;  // 这里直接z取反来朝`_CameraForward`反方向
        posWS = farPlaneWS  + z * _CameraForward;
        
      1. 透视相机的情况:

      使用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

最后的源码链接没忘:https://github.com/LazyunGame/URP_RenderFeature_Fog