Loading... #### 使用RenderFeature分别实现了透视相机和正交相机的高度雾和远景雾 如图: 透视相机 ![透视相机下的远景雾](https://res.lazyun.cn/typecho/2022/05/21/persp_far_fog.jpg?x-oss-process=style/compress) ![透视相机下的高度雾](https://res.lazyun.cn/typecho/2022/05/21/ortho_deep_fog.jpg?x-oss-process=style/compress) 正交相机 ![透视相机下的远景雾](https://res.lazyun.cn/typecho/2022/05/21/ortho_far_fog.jpg?x-oss-process=style/compress) ![透视相机下的高度雾](https://res.lazyun.cn/typecho/2022/05/21/persp_deep_fog.jpg?x-oss-process=style/compress) --- ### 如何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重建世界坐标,这个有很多的实现方式,比如: - [Unity Shader入门精要第十五章提到的方式](https://github.com/candycat1992/Unity_Shaders_Book/blob/master/Assets/Shaders/Chapter15/Chapter15-FogWithNoise.shader) - [Unity URP官方手册](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.1/manual/writing-shaders-urp-reconstruct-world-position.html) ##### 4. 通过对这些方式的学习有了下面的方案: - 从屏幕Mesh面片UV重构NDC坐标,并结合传入的`_ClipToWorldMatrix` 可以得到摄像机远平面(Far Plane)上的世界坐标, 记为`farPlaneWS` > 这里我写了一个[TestCameraPlane.shader](https://github.com/LazyunGame/URP_RenderFeature_Fog/blob/master/Assets/FogRenderFeature/Resources/TestCameraPlane.shader) 来验证得到下图 ![](https://res.lazyun.cn/typecho/2022/05/21/ortho_far.jpg?x-oss-process=style/compress)![](https://res.lazyun.cn/typecho/2022/05/21/persp_far.jpg?x-oss-process=style/compress) - 然后通过 `farPlaneWS - _CameraWorldPos` 获得从相机到远平面上的点的世界坐标系向量,这里需要注意的是: - 1. 这个向量只用于透视相机情况下的世界坐标重建,正交相机用不到这个值,如果使用环境只有正交相机,那就可以将这个值相关的代码删除提高性能 - 2. 这个向量不是单位向量,还包含了长度,就是**相机到对应远平面点的距离** - 3. 为了提高性能,这个向量放在了顶点着色器计算,其他面片上 非顶点的点的向量会被插值器自动处理 - 最后进入片元着色器中进行对应场景内模型的点世界坐标的重建,这里要分2种情况: - 1. 正交相机情况: > 使用远平面的世界坐标当作起始点,朝`_CameraForward`反方向运动一定距离z 就是对应点的世界坐标了,很明显,这里的z 就需要使用Depth获得, 具体代码如下: ``` float z = - ( far - near ) * depth; // 这里直接z取反来朝`_CameraForward`反方向 posWS = farPlaneWS + z * _CameraForward; ``` - 2. 透视相机的情况: > 使用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 最后修改:2022 年 05 月 21 日 © 禁止转载 赞 0
1 条评论
您是!我的!神!救命了|´・ω・)ノ