Unity 几种常见合批手段的要求
概述
合批(Batching)是 Unity 渲染优化的核心手段,目的是将多个绘制调用(Draw Call)合并为一个,减少 CPU 与 GPU 之间的通信开销。Unity 中常见的合批手段有四种:
- SRP Batcher
- GPU Instancing
- 动态合批(Dynamic Batching)
- 静态合批(Static Batching)
每种合批方式都有严格的前提要求和互斥关系,理解这些是正确使用它们的前提。
1. SRP Batcher
原理
SRP Batcher 通过复用 CPU 端的Material数据缓冲区,让 GPU 在同一绘制循环中快速切换材质数据,而不需要重新绑定 Shader 和 Constant Buffer。适用于大量使用相同 Shader 变体但不同材质属性的渲染对象。
前提要求
| 要求 | 说明 |
|---|---|
| 渲染管线 | 必须使用SRP(URP / HDRP / 自定义 SRP),Built-in Render Pipeline 不支持 |
| Shader | 必须编写兼容 SRP Batcher的 Shader。所有材质属性要通过CBUFFER声明(SRP 核心库中的 Shader 默认满足) |
| Shader 变体 | 合批的对象必须使用同一个 Shader 变体(即 Shader 文件和 keywords 组合完全一致) |
| 平台 | 所有支持 DX10+(SM 4.0+)的平台均可 |
| 开启 | 在 Project Settings > Graphics > SRP Batcher 中开启(默认开启) |
不满足时的行为
如果某个 Material 使用了不兼容 SRP Batcher 的 Shader,该 Material 会回退到传统渲染路径,不会影响其他兼容对象的 SRP Batcher 合批。
与 GPU Instancing 的关系
互斥。如果材质开启了Enable GPU Instancing,Unity 会优先走 GPU Instancing,不会走 SRP Batcher。
2. GPU Instancing
原理
GPU Instancing 将多个相同 Mesh、相同 Material的渲染实例打包为一个 Draw Call,数据以 Instance Buffer 形式传给 GPU,由 GPU 一次性绘制。适合大量重复物体如草、树、子弹、粒子等。
前提要求
| 要求 | 说明 |
|---|---|
| Shader | Shader 必须声明#pragma multi_compile_instancing,并在顶点着色器中调用UNITY_VERTEX_INPUT_INSTANCE_ID/UNITY_SETUP_INSTANCE_ID |
| Mesh | 必须完全相同(同一个 Mesh 引用) |
| Material | 必须使用同一个 Material 实例(同一个引用,而非属性相同) |
| 批次数量 | 单个 Draw Call 的实例数量受 GPU 限制(通常 500~1023 个实例) |
| 开启 | Material Inspector 中勾选Enable GPU Instancing |
额外说明
- 每个实例可以通过
MaterialPropertyBlock传递不同的属性(如颜色、缩放等),但MaterialPropertyBlock的字段必须在 Shader 中声明为UNITY_INSTANCING_BUFFER_START/END。 - 不支持LOD 组内不同 LOD 级别的合批(不同 LOD 模型不同,无法 Instancing)。
- 如果 Mesh 使用 SkinnedMeshRenderer,需要额外的兼容处理(部分平台不支持)。
互斥关系
| 合批方式 | 是否兼容 |
|---|---|
| SRP Batcher | ❌ 互斥,开启 GPU Instancing 后不走 SRP Batcher |
| 动态合批 | ❌ 互斥 |
| 静态合批 | ❌ 互斥,但物理上不太会同时使用 |
3. 动态合批(Dynamic Batching)
原理
Unity 在 CPU 端将多个小 Mesh 合并为一个大的 Vertex Buffer,然后一次性提交 GPU 绘制。Unity 会自动检测满足条件的渲染器在每帧进行合并。
前提要求
| 要求 | 说明 |
|---|---|
| 顶点数 | 单个 Mesh 的顶点数≤ 300,<=900个顶点属性(Shader 中如果使用了某些属性会增加顶点计算量,实际会更严格) |
| UV 通道 | 最多支持UV 0 和 UV 1,如果使用 UV 2、UV 3 则无法动态合批 |
| 光照 | 如果使用多 Pass Shader(包含多个 ForwardBase/ForwardAdd Pass),动态合批会失效 |
| 材质 | 参与合批的物体必须使用完全相同的 Material(同一个引用) |
| 变换 | 物体不能同时受多个缩放因子不一致的父级影响,Uniform Scale 环境下表现最佳 |
| 镜像变换 | 负缩放(Scale 为负数)会导致法线计算错误,动态合批会自动跳过 |
性能代价
动态合批是在CPU 端每帧合并顶点数据,对于顶点数较多的物体反而会拖慢性能。因此它只适合少量小顶点物体(如粒子、小道具)。
互斥关系
| 合批方式 | 是否兼容 |
|---|---|
| SRP Batcher | 可以共存,但如果一个物体走动态合批就不会走 SRP Batcher |
| GPU Instancing | ❌ 互斥 |
| 静态合批 | ❌ 互斥,同一物体不会同时参与两者 |
4. 静态合批(Static Batching)
原理
在构建时(Build / Baking)将标记为Batching Static的物体合并到一个大的 Vertex Buffer 中,并生成统一的 Index Buffer。运行时直接作为一个 Mesh 提交绘制。
前提要求
| 要求 | 说明 |
|---|---|
| Static 标记 | 物体必须勾选Static或单独勾选Batching StaticFlag |
| 材质 | 参与合批的物体必须使用相同的 Material |
| 移动 | 物体在运行时不能移动、旋转、缩放 |
| 内存 | 静态合批会创建合并后的大 Mesh,会显著增加 Build 后包体和运行时内存 |
| 顶点属性 | 不同 Mesh 可以有不同顶点格式,Unity 会自动补齐 |
注意事项
- 静态合批会禁用 GPU Instancing(因为 Mesh 已经被合并了)。
- 参与静态合批的物体仍然可以配合Lightmap、Occlusion Culling使用。
- 如果场景中有大量重复物体(如路灯),静态合批后的 Mesh 无法利用 GPU Instancing,反而可能不如单独走 Instancing。
互斥关系
| 合批方式 | 是否兼容 |
|---|---|
| SRP Batcher | 可以共存。静态合批只是合并几何数据,渲染时仍走 SRP Batcher 管线 |
| GPU Instancing | ❌ 互斥。合批后的 Mesh 无法 Instancing |
| 动态合批 | ❌ 互斥。静态合批优先级更高 |
5. 合批对比总表
| 特性 | SRP Batcher | GPU Instancing | 动态合批 | 静态合批 |
|---|---|---|---|---|
| 适用管线 | URP / HDRP | All | All | All |
| 合批时机 | 运行时(GPU 端) | 运行时(GPU 端) | 运行时(CPU 端) | 构建时 |
| 相同 Mesh | 不需要 | 必须相同 | 不需要 | 不需要 |
| 相同 Material | 不需要(相同 Shader 变体即可) | 必须相同引用 | 必须相同 | 必须相同 |
| 顶点限制 | 无 | 无 | ≤ 300 | 无 |
| 内存/CPU 开销 | 低 | 低 | 高(CPU 合并) | 中(构建时增加内存) |
| 运行时移动 | 可以 | 可以 | 可以 | ❌ 不可以 |
| 与 SRP Batcher 兼容 | — | ❌ 互斥 | ✅ 共存但二选一 | ✅ 共存 |
| 与 GPU Instancing 兼容 | ❌ 互斥 | — | ❌ 互斥 | ❌ 互斥 |
| 推荐场景 | 大量不同材质但同 Shader 的对象 | 大量相同物体(草、树、子弹) | 少量小顶点物体 | 完全静止的环境物体 |
6. 实际使用建议
- 优先使用 SRP Batcher(URP/HDRP 项目默认开启),它是最通用、约束最少、CPU 开销最低的方案。
- 重复物体用 GPU Instancing,尤其是草地、树木、弹壳、粒子等大量相同 Mesh 的场景。
- 动态合批只用于 UI 或极小物体,顶点数必须确认 ≤ 300,且不要跟 GPU Instancing 混用。
- 静态合批在老项目或 Built-in 管线中还有价值,但在 SRP 管线中已被 SRP Batcher 部分替代,且内存代价较高,需谨慎使用。
- 不要同时开启 SRP Batcher 和 GPU Instancing(同一材质会互斥),但可以同时开启 SRP Batcher 和静态合批。