Chapter 3 Batching 的优势

这章主要讨论动态批处理(Dynamic Batching)和静态批处理(Static Batching)。GPU Instancing 和 SRP 稍后再讨论。

1. Draw Calls

  • Draw Call:CPU 向 GPU 发送的渲染指令。
  • 主要分两步:

    1. 上传纹理数据和网格数据到 GPU
    2. 使用纹理数据设置网格的渲染
  • GPU 在切换渲染状态时消耗很大,SetPass Call 显示切换次数,比 Draw Call 还重要。

2. Materials 和 Shaders

  • 每个材质球(Material)引用一个 shader,并定义该 shader 使用的属性值(如颜色、贴图)。
  • 当多个对象使用相同的 Material 时,它们更容易被合批,因为 GPU 不需要频繁切换 Render State。
  • 即使两个 Material 引用同一个 shader,只要它们的属性值不同(例如不同的主贴图),通常也会被视为不同的 Material,从而无法合批。
  • 因此,减少 Material 数量、共享 Material 和使用图集(Atlas)是降低 Draw Call 的重要手段。

3. The Frame Debugger

使用 Frame Debugger 可以帮助我们分析渲染状态,找出不能 batching 的原因。打开方式:Window | Analysis | Frame Debugger。它会逐步显示每一帧的绘制调用,帮助观察:

  • 每个对象使用了哪个 shader 和 Material
  • 哪些对象被合批,哪些没有
  • 不能合批的具体原因(如不同 Material、不同缩放、顶点属性过多等)

Frame Debugger 示例

4. 动态批处理

  • 特点:

    • 运行时进行合批
    • 可见的物体才可能进行合批
    • 可动的物体也可以合批
  • 需满足的条件:

4.1 顶点属性要求

  • 动态合批要求顶点数不能超过 300 个。
  • 注意:Unity 加载的模型顶点数和模型原数据顶点数可能不一样,所以最好在 Unity 的 Inspector Preview 窗口中确认顶点数。
  • 动态合批要求顶点属性不能超过 900。顶点属性计算:顶点数 × 每个顶点上的属性数。如果使用的 Shader 有 5 个属性,那么顶点数就不能超过 900 / 5 = 180 个。

4.2 Mesh Scaling(网格缩放)要求

  • 带有负缩放的对象不能与全为正数缩放的对象一起合批,但可以和其他具有相同数量负轴缩放的对象合批。例如 (-1, 1, 1) 可以和 (1, -1, 1) 合批。

4.3 动态合批要点

  • 适用于大量简单模型的场景。
  • 如果因为使用不同贴图导致不能合批,可以考虑将贴图合并到一张图中,并重新展 UV。
  • 多使用 Frame Debugger 分析不能合批的情况。
  • 有时可以调整不同材质物体间的渲染顺序来合批(比如草和石头在一起,可以先渲染草再渲染石头,让同类物体都能动态合批)。

5. 静态合批

5.1 合批要求

  • GameObject 需要勾选 Static 选项。
  • 需要额外的内存来存放静态批处理后的结果网格。
  • 合批的顶点总数有限制:32000–64000 之间。
  • 合批对象可以是不同的网格,但必须使用相同的材质球。

5.2 内存问题

  • 使用静态合批很有可能增加内存。
  • 因为静态合批会把需要合批对象的网格复制一份。如果有 1000 个相同的树,使用静态合批后的内存可能是不使用时的 1000 倍。

5.3 其他需要注意的点

  • Draw Call 的降低只能在运行中看到。
  • 运行中动态创建并标记为 Static 的对象不会自动被合批。
  • 但我们可以使用 StaticBatchUtility.Combine() 对动态创建的对象进行静态合批:

    • 需要注意,这个方式消耗很大,不宜在流畅度敏感期间使用。
    • 如果使用 StaticBatchUtility.Combine() 的对象没有被标记为 Static,那么它其实是可以被移动的(除了它的网格)。也就是说,可能会移动了 Collider,但网格还停留在原地。这种情况容易产生不期望的 Bug,需要额外注意。

Copyright © 原书作者与译者。基于 Unity Game Optimization 第3版中文翻译整理。

This site uses Just the Docs, a documentation theme for Jekyll.