URP 渲染实战 / 第 21 章 UV 动画与粒子 / 案例 21.6
案例 21.6软粒子:与场景的柔和相交进阶
目标:消除粒子面片插进地面、墙壁时那条生硬的直线。
粒子是平面,场景几何体是实体;面片与地面相交,深度测试会把地面以下的部分整齐切掉,留下一条直线,立刻暴露出“这是一张纸片”。软粒子(Soft Particles)的思路是:在片元着色器里读取场景深度,比较“场景表面的距离”与“粒子自身的距离”,两者越接近(粒子越要插进物体),alpha 越小:
两个深度都必须是线性的观察空间深度(单位:米)。场景深度来自 _CameraDepthTexture,用 SampleSceneDepth 读出的是非线性的设备深度,要经过 LinearEyeDepth(raw, _ZBufferParams) 转换;粒子自身的观察空间深度在顶点着色器里直接取 −positionVS.z 传下来(Unity 观察空间看向 −Z)。d 是过渡距离,代码里的 _SoftDistance。
作为对照,摘自 URP 14 ShaderLibrary/Particles.hlsl 的实现(注意它支持正交相机,还多了一个 near 偏移参数):
float SoftParticles(float near, float far, float4 projection) { float fade = 1; if (near > 0.0 || far > 0.0) { float2 uv = UnityStereoTransformScreenSpaceTex(projection.xy / projection.w); uv = FoveatedRemapLinearToNonUniform(uv); float rawDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, uv).r; float sceneZ = (unity_OrthoParams.w == 0) ? LinearEyeDepth(rawDepth, _ZBufferParams) : LinearDepthToEyeDepth(rawDepth); float thisZ = LinearEyeDepth(projection.z / projection.w, _ZBufferParams); fade = saturate(far * ((sceneZ - near) - thisZ)); } return fade; }

软粒子需要深度图URP 只有在 URP Asset 勾选了 (或相机单独开启、或某个 Renderer Feature 请求了深度)时才会生成
_CameraDepthTexture;否则采到的是默认值,所有粒子要么全透明要么毫无变化。URP 的粒子材质面板会在这种情况下给出提示,自写 Shader 则不会,排查时先看 Frame Debugger 里有没有 CopyDepth 这一步。另外,在移动端生成深度图本身有带宽开销(可能需要一次额外的 Depth Prepass 或拷贝),大量使用软粒子前要评估。同样的“深度差”还可以做近相机淡出:粒子离相机太近时会糊满整个屏幕,带来巨大的 Overdraw,按 thisZ 在近处淡出即可(代码中的 _CAMERAFADE_ON 分支)。它不需要深度图,开销几乎为零,烟雾类粒子建议默认打开。