贴子标题/内容
(Unity)物体与地形融合
<p><img src="https://pic2.zhimg.com/v2-12267e99ee6bdb386f394b4d61400817_1440w.jpg" alt="" data-href="" style="width: 100%;"/></p><p>开启效果</p><p><img src="https://pic4.zhimg.com/v2-44a5ba4d2f1684416bdeefe229135853_1440w.jpg" alt="" data-href="" style="width: 100%;"/></p><p>关闭效果</p><p style="text-align: start;">Git地址:<a href="https://link.zhihu.com/?target=https%3A//github.com/Yami1207/GameFramework-Unity" target="_blank">Yami1207/GameFramework-Unity (github.com)</a></p><p style="text-align: start;">Demo场景:</p><p style="text-align: start;"><img src="https://pic3.zhimg.com/v2-438469606fcb24ecea3f3f305c73e96c_1440w.jpg" alt="" data-href="" style="width: 100%;"></p><h2 style="text-align: start; line-height: 1.5;">简述</h2><p style="text-align: start;">模拟UE的PDO(像素深度偏移量),用于处理物体与地形之间衔接生硬的过渡效果。原理是预先保存地形信息,渲染模型时通过与地形的深度差进行插值,从而形成过渡效果。</p><h2 style="text-align: start; line-height: 1.5;">渲染地形信息</h2><p style="text-align: start;">在渲染物体前,通过RenderFeature将地形颜色、深度和法线信息保存到RT上。由于一张RT最多只有4个通道,要同时收集颜色和法线就需要使用MRT(Multiple Render Target)。下面粘贴相关代码(PixelDepthOffset.cs)</p><h3 style="text-align: start; line-height: 1.5;">设置MRT</h3><p><br></p><pre><code class="language-csharp">private RenderTargetIdentifier[] m_MultipleRenderTargets = new RenderTargetIdentifier[2]; public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { int width = cameraTextureDescriptor.width &gt;&gt; (int)m_Owner.m_TexQuality; int height = cameraTextureDescriptor.height &gt;&gt; (int)m_Owner.m_TexQuality; RenderTextureDescriptor desc = cameraTextureDescriptor; desc.colorFormat = RenderTextureFormat.ARGBHalf; desc.width = width; desc.height = height; desc.msaaSamples = 1; cmd.GetTemporaryRT(s_PDOAlbedoTexPropID, desc, FilterMode.Bilinear); RenderTextureDescriptor normalDesc = cameraTextureDescriptor; normalDesc.colorFormat = RenderTextureFormat.ARGB32; normalDesc.width = width; normalDesc.height = height; normalDesc.useMipMap = false; normalDesc.depthBufferBits = 0; normalDesc.msaaSamples = 1; normalDesc.sRGB = false; cmd.GetTemporaryRT(s_PDONormalTexPropID, normalDesc); m_MultipleRenderTargets[0] = s_PDOAlbedoTexPropID; m_MultipleRenderTargets[1] = s_PDONormalTexPropID; } </code></pre><p style="text-align: start;">注:由于需要保存深度,当RT使用ARGB32会出现精度不足导致闪烁,所以这里使用了ARGBHalf。</p><h3 style="text-align: start; line-height: 1.5;">渲染地形</h3><p><br></p><pre><code class="language-csharp"> public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { ... // 设置渲染Layer m_FilteringSettings.layerMask = m_Owner.m_CullMask; // 只渲染不透明物 m_FilteringSettings.renderQueueRange = RenderQueueRange.opaque; DrawingSettings drawingOpaqueSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.CommonOpaque); context.DrawRenderers(renderingData.cullResults, ref drawingOpaqueSettings, ref m_FilteringSettings); ... } </code></pre><h2 style="text-align: start; line-height: 1.5;">物体与地形过渡</h2><p style="text-align: start;">渲染物体时,通过物体与地形之间的深度差,得到颜色与法线再进行光照计算</p><p style="text-align: start;"><br></p><pre><code class="language-cpp">inline void MixPixelDepthOffset(float3 positionWS, half differ, inout CustomSurfaceData surfaceData, inout CustomInputData inputData) { // 由于使用同一个相机,可用裁剪空间坐标做为UV float4 positionCS = TransformWorldToHClip(positionWS); half2 uv = 0.5 * (positionCS.xy / positionCS.w) + 0.5; #if UNITY_UV_STARTS_AT_TOP uv.y = 1 - uv.y; #endif // albedo float4 albedoColor = SAMPLE_TEXTURE2D(_G_PDO_AlbedoTex, sampler_G_PDO_AlbedoTex, uv); float depth = LinearEyeDepth(positionWS, GetWorldToViewMatrix()); half t = smoothstep(0, differ, albedoColor.a - depth); //saturate((albedoColor.a - depth) / differ); surfaceData.albedo = lerp(albedoColor.rgb, surfaceData.albedo, t); // nromal float3 normalWS = SAMPLE_TEXTURE2D(_G_PDO_NormalTex, sampler_G_PDO_NormalTex, uv).rgb; normalWS = 2.0 * normalWS - 1.0; inputData.normalWS = lerp(normalWS, inputData.normalWS, t); } </code></pre><p><br></p>