GameTech/URP 渲染实战

案例 29.1合批友好的 Shader 与反例入门

目标:写一个同时兼容 SRP Batcher、GPU Instancing 和 LOD Cross-Fade 的最小 Shader,再写一个反例,学会在编辑器里验证。

Assets/Cookbook/Ch29/BatcherFriendly.shader(节选:cbuffer)
TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap);
// SRP Batcher:每个材质一份常量缓冲,布局在所有变体里必须一致
CBUFFER_START(UnityPerMaterial)
    float4 _BaseMap_ST;
    half4 _BaseColor;
CBUFFER_END

// GPU Instancing:允许用 MaterialPropertyBlock 逐实例覆盖颜色
UNITY_INSTANCING_BUFFER_START(PerInstance)
    UNITY_DEFINE_INSTANCED_PROP(half4, _InstanceColor)
UNITY_INSTANCING_BUFFER_END(PerInstance)
uniform sampler2D _BaseMap;
// SRP Batcher:每个材质一份常量缓冲,布局在所有变体里必须一致
uniform vec4 _BaseMap_ST;
uniform vec4 _BaseColor;

// GPU Instancing:允许用 MaterialPropertyBlock 逐实例覆盖颜色
uniform vec4 _InstanceColor;

反例只差一行——属性声明在 CBUFFER 之外:

Assets/Cookbook/Ch29/BatcherBreaker.shader(节选:bad)
half4 _BaseColor;     // ✗ 不在 UnityPerMaterial 里
uniform vec4 _BaseColor;     // ✗ 不在 UnityPerMaterial 里

逐实例颜色通过 MaterialPropertyBlock 设置:

Assets/Cookbook/Ch29/PerInstanceTint.cs(节选:apply)
void Apply()
{
    var r = GetComponent<Renderer>();
    if (r == null) return;
    s_Block ??= new MaterialPropertyBlock();
    r.GetPropertyBlock(s_Block);
    s_Block.SetColor(s_InstanceColor, tint);
    r.SetPropertyBlock(s_Block);
}

如何验证:

  1. 在 Project 窗口选中 Shader,Inspector 里会显示 SRP Batcher: compatible 或 not compatible,不兼容时附带原因。上面的反例会提示类似 Material property is found in another cbuffer than "UnityPerMaterial";如果不同 Pass 的 UnityPerMaterial 内容不一致,原因是 UnityPerMaterial CBuffer inconsistent size inside a SubShader。
  2. 打开 Window › Analysis › Frame Debugger,Enable 后展开 DrawOpaqueObjects。兼容的物体会被归入名为 SRP Batch 的事件,选中后右侧会说明这一批为什么没能和上一批合并,例如 SRP: Node have different shaders.、SRP: Node use different shader keywords、SRP: Node is not compatible with SRP batcher、SRP: Node use multi-pass shader。
  3. 对非 SRP Batcher 的绘制(Instancing、静态/动态合批),Frame Debugger 同样会给出不能合批的原因,常见的有 Objects have different materials.、Objects have different MaterialPropertyBlock set.、Objects are affected by different light probes.、Objects are lightmapped. 等。
提示“同一 Shader 变体”是 SRP Batcher 连续合批的关键。材质之间关键字不同(例如一个开了 _NORMALMAP、一个没开)会让它们落到不同变体,Frame Debugger 里就会看到 Node use different shader keywords。整理材质、统一关键字,往往比改 Shader 更能提升合批效果。另外,URP 的不透明物体排序是“由近到远”优先的(支持隐藏面消除的 GPU 上会放宽,见 CameraData.defaultOpaqueSortFlags 的注释),排序也会打断同 Shader 的连续性。