Unity 渲染优化全解析:Draw Call、Batch、SetPass 与批处理技术
Unity 渲染优化是游戏开发的核心环节,其中 Draw Call、Batching 和 SetPass calls 是关键指标。Draw Call 指 CPU 向 GPU 发送渲染命令的次数,过多会导致 CPU 瓶颈;SetPass calls 指切换渲染状态(如材质/Shader)的次数,常与 Draw Call 相关;Batching 是合并多个对象渲染的技术,减少这些调用。优化这些可显著提升帧率(FPS),尤其在移动/VR 项目中。根据 Unity 官方文档和开发者指南,批处理可将 Draw Call 减少 50-90%,但需注意材质兼容和动态物体。
1. Draw Call 详解
定义:Draw Call 是 CPU 命令 GPU 渲染一组顶点/索引的调用。每个独特材质/网格组合通常产生一个 Draw Call。
成因与影响:
- 高 Draw Call 原因:不同材质物体、动态阴影、粒子系统、多相机渲染。
- 性能瓶颈:CPU 忙于准备命令,GPU 空闲等待;目标 <300 Draw Call/帧(移动端 <100)。
- 监控:Window > Rendering > Stats > Batches/SetPass calls(SetPass ≈ Draw Call 在非批处理场景)。
优化原则:减少独特渲染状态,合并相似物体。
2. SetPass Call 详解
定义:SetPass Call 是 GPU 切换渲染状态的次数,每次涉及材质/Shader 变更,常导致批处理中断。
与 Draw Call 关系:每个 SetPass 后可能跟多个 Draw Call;SetPass > Draw Call 表示批处理失败。
成因:
- 材质变体过多(动态关键词)。
- 透明物体排序(Render Queue 变更)。
优化:统一材质、使用 SRP Batcher(URP/HDRP 原生支持)减少变体。
3. 批处理技术详解
批处理(Batching)合并多个 Draw Call 为一个,减少 SetPass。Unity 提供三种主要技术:
静态批处理(Static Batching)
- 原理:预合并静态物体网格/材质为单一批次,运行时无开销。
- 适用:不动物体(如建筑),相同材质。
- 设置:Edit > Project Settings > Player > Other Settings > Static Batching = Enabled。
- 代码示例(运行时检查):
using UnityEngine;
public class StaticBatchChecker : MonoBehaviour
{
void Start()
{
// 手动静态批处理(编辑器/构建)
#if UNITY_EDITOR
StaticBatchingUtility.Combine(gameObject); // 合并子物体
#endif
Debug.Log("静态批处理启用:" + PlayerSettings.useStaticBatching);
}
}
- 注意:合并后物体不可移动;内存增加(复制网格)。
动态批处理(Dynamic Batching)
- 原理:运行时合并相似动态物体(顶点 <300,相同材质)。
- 适用:小物体群(如子弹、粒子)。
- 设置:Player Settings > Dynamic Batching = Enabled。
- 代码示例(强制合并):
using UnityEngine;
public class DynamicBatcher : MonoBehaviour
{
public MeshRenderer[] renderers; // 要批处理的物体
void Start()
{
// 统一材质/网格
Material sharedMat = renderers[0].sharedMaterial;
foreach (var r in renderers)
{
r.sharedMaterial = sharedMat;
}
Debug.Log("动态批处理启用:" + PlayerSettings.useDynamicBatching);
}
}
- 注意:仅支持简单网格(顶点 <300,材质无复杂关键词);动态物体需相同 Transform Scale。
SRP Batcher(URP/HDRP 专用)
- 原理:批处理材质变体(Shader 关键词),减少 SetPass calls,而非合并网格。
- 适用:动态/静态物体,相同 Shader 但不同材质属性。
- 设置:URP/HDRP Asset > Advanced > SRP Batcher = Enabled(默认)。
- 代码示例(检查兼容):
using UnityEngine.Rendering;
public class SRPBatcherChecker : MonoBehaviour
{
void Start()
{
var pipeline = GraphicsSettings.currentRenderPipeline;
if (pipeline != null && pipeline.GetType() == typeof(UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset))
{
Debug.Log("SRP Batcher 启用:" + ((UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset)pipeline).supportsSRPBatcher);
}
}
}
- 注意:Shader 需兼容 SRP Batcher(无运行时变体);移动端高效。
GPU Instancing
- 原理:相同网格/材质的物体单次 Draw Call 渲染多个实例。
- 设置:材质 Inspector > Enable GPU Instancing = On。
- 代码示例:
using UnityEngine;
public class GPUInstancer : MonoBehaviour
{
public Material instanceMaterial; // 启用 Instancing 的材质
void Start()
{
instanceMaterial.enableInstancing = true;
// 动态实例化
for (int i = 0; i < 100; i++)
{
GameObject obj = Instantiate(prefab);
obj.GetComponent<MeshRenderer>().material = instanceMaterial;
obj.transform.position = Random.insideUnitSphere * 10f;
}
}
}
- 注意:支持动态物体;URP/HDRP 自动优化。
4. 批处理性能测试工具
public class BatchTester : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
LogBatchStats();
}
}
void LogBatchStats()
{
Debug.Log($"Draw Calls: {UnityEngine.Rendering.RenderPipelineManager.drawCallsCount}");
Debug.Log($"SetPass Calls: {UnityEngine.Rendering.RenderPipelineManager.setPassCount}");
Debug.Log($"Batches: {UnityEngine.Rendering.RenderPipelineManager.batchCount}");
}
}
总结与最佳实践
- 总体策略:优先 SRP Batcher(现代管线)+ GPU Instancing;静态物体用 Static Batching;动态用 Dynamic/Mesh Combine。
- 材质优化:统一 Shader 关键词,减少变体(Shader Graph > Variants > Prune Unused)。
- 测试:Profiler > Rendering > Stats(监控 Draw/SetPass);目标 <300 Draw Call/帧。
- 工具:Unity Batch Analyzer(Asset Store 免费)自动诊断。
通过这些技术,可将复杂场景的 Draw Call 优化到最小,提升游戏流畅度。