贴子标题/内容
(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 >> (int)m_Owner.m_TexQuality;
int height = cameraTextureDescriptor.height >> (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>