45.动态批处理

45.性能优化-CPU-图形-动态批处理


45.1 知识点

前置准备

本次学习建议使用内置渲染管线项目,而非 URP 项目。因为SRP(URP/HDRP)更推荐 SRP Batcher。

动态批处理是什么

动态批处理(Dynamic Batching) 是 Unity 内置渲染管线中的一项 CPU 端优化技术。它会在运行时把多个使用相同材质的动态物体网格数据合并到一次 DrawCall 中,从而减少 DrawCall 调用次数,降低 CPU 提交渲染命令的开销。

它的 3 个重要特征:

  1. 批处理在运行时生成。
  2. 批次中的对象可能在不同帧变化(取决于主摄像机可见网格)。
  3. 场景中运动的对象也可以批处理。

原理概述:
渲染时原本每个物体都要一次 DrawCall(CPU 把网格、材质数据提交给 GPU)。假设 3 个相同材质的 3D 对象需要 3 次 DrawCall;开启动态批处理且满足条件时,Unity 会把这 3 个对象合并成一个更大的网格缓冲区,并通过一次 DrawCall提交给 GPU,从而减少 DrawCall。由于合并每帧都要重新计算批次,所以称为“动态”。

说人话:
动态批处理就是 Unity 在运行时由 CPU 自动把满足限制条件的多个动态物体网格合并成一个大网格,一次性提交给 GPU,以减少 DrawCall 的技术。

注意:

  1. 动态批处理是内置渲染管线的重要优化手段;SRP(URP/HDRP)更推荐 SRP Batcher(后续再讲)。
  2. Unity 创建一个对象时,DrawCall 不一定只增加 1 次,阴影、深度贴图、多 Pass、透明、子网格、多材质都会叠加 DrawCall。

操作实践(理解 DrawCall 变化):
在内置渲染管线项目中创建新场景,创建一个红色材质赋给 Cube,并复制出 3 个 Cube。

隐藏所有 Cube 时默认有 2 次 DrawCall。

激活 1 个 Cube 后 DrawCall 增加(例如变为 7 次),帧调试器显示除物体渲染外还有阴影深度等额外 DrawCall。


激活 2 个、3 个 Cube 后 DrawCall 继续增加(例如变为 11、15)。


未开启动态批处理时,3 个 Cube 会分别各 1 次 DrawCall;我们的动态批处理的作用就是要把3次DrawCall合成1次DrawCall。

如何开启动态批处理

内置渲染管线:
Player Settings → Other Settings → Rendering → Dynamic Batching

URP 管线:
URP 更倾向使用 SRP Batcher;在 Unity 2023 以后版本中,URP 对动态批处理支持已很少使用。若要使用动态批处理,需要切换为内置渲染管线。

操作实践:
开启动态批处理选项后,可以看到 DrawCall 数量下降,帧调试器显示 3 个 Cube 被合并渲染。

开启后,可以看到DrawCall数量下降,帧调试器中是三个Cube一起渲染

动态批处理的限制

1. 硬性条件

  • 顶点数限制:<= 225(Unity 2021 之前为 <= 300)。
  • 顶点属性限制:顶点数 * 顶点属性数 <= 900

解释:
每个网格最多 225300 个顶点,且 Shader 使用的顶点属性总量不超过 900
常见顶点属性有:位置、法线、切线、多套 UV、顶点颜色、骨骼权重、骨骼索引等。

示例:

  • 简单 Shader 常用 3 个属性(位置、法线、UV),225 * 3 = 675300 * 3 = 900 满足条件。
  • 若 Shader 使用 5 个属性,则顶点上限为 900 / 5 = 180

只要超出顶点或属性任一限制,该网格不会被动态批处理。

2. 相同材质实例
不同对象必须使用同一个 Material 实例(同一内存引用),材质属性、Shader 关键字、渲染状态必须一致。

3. 不能是 SkinnedMeshRenderer
骨骼动画模型每帧 CPU 做蒙皮变形,不适合合并;动态批处理只支持 MeshRenderer 和 ParticleSystem。

4. 变换不能包含镜像
正比例缩放对象与负比例缩放对象不能放在同一批次。

5. 相同光照状态
使用光照贴图时必须保持同一个光照贴图;光照探针、反射探针、雾效开关等也需一致,否则状态切换会打断批处理。

6. 同一渲染队列
Render Queue 必须一致,例如都在不透明队列;否则会被排序拆开。

7. 材质不能开启 GPU Instancing
启用 GPU Instancing 后会走 Instancing 路径,与动态批处理互斥。

8. Shader 尽量单 Pass
多 Pass 会带来额外 DrawCall。

操作实践(理解限制):
0给其中一个 Cube 换蓝色材质,DrawCall 增加,帧调试器多了一次绘制。

创建三个球体并设置红色材质,发现不走动态批处理,因为球体顶点数超限制。

把球体插入 Cube 中间,移动位置到特定深度时可能打断批处理,和渲染顺序有关。


可以看到动态批处理失败的常见原因之一:材质球不同。

适合使用动态批处理的情况

适合:

  • 大量使用相同材质的小型动态网格,且满足批处理条件,顶点数低于 225(300)/900 限制。
  • 物体是动态的(位置、旋转、缩放会变化),无法使用静态批处理时优先考虑动态批处理。

使用场景:

  1. 游戏中的小道具、小物件(金币、宝石、道具箱、掉落物)。
  2. 简单动态环境元素(旋转风扇叶片等)。
  3. 低顶点数的动态建筑元素(小路灯、小栅栏、路标等)。
  4. 网格粒子(Mesh Particle),若未使用 GPU Instancing 也可走动态批处理。

不太适合:

  1. 网格顶点数大(超 225(300) 或超 900 属性限制)。
  2. 多 Pass Shader(描边、ForwardAdd 多光源、阴影等)。
  3. 材质多样化(不同材质实例会切断批处理)。
  4. Skinned Mesh(角色、骨骼动画模型)。
  5. URP/HDRP 新版本优先使用 SRP Batcher + GPU Instancing。

45.2 知识点代码

Lesson45_性能优化_CPU_图形_动态批处理.cs

public class Lesson45_性能优化_CPU_图形_动态批处理
{
    #region 知识点一 动态批处理是什么?

    //动态批处理(Dynamic Batching) 
    //是一种由Unity自动完成的CPU端优化技术
    //它会在运行时把多个使用相同材质的动态物体的网格数据合并到一次DrawCall中
    //从而减少DrawCall的调用次数,降低CPU提交渲染命令的开销
    //它的3个重要优势:
    //1.批处理在运行时生成
    //2.批处理中包含的对象在不同帧之间可能有所不同
    //  取决于哪些网格在主摄像机视图中当前可见
    //3.场景中运动的对象也可以批处理

    //它的主要原理是
    //Unity在渲染时,原本需要将每个物体的网格、材质数据通过CPU发送给GPU,即一次DrawCall
    //假设3个相同材质的3D对象(网格可不同,材质需相同),渲染他们需要3次DrawCall
    //但是如果在开启动态批处理时,并且这些对象满足动态批处理的限制条件
    //那么动态批处理会将这3个对象合并为一个更大的网格缓冲区,并和相同的材质一起 通过一次DrawCall提交给GPU
    //从而达到减少DrawCall的目的
    //注意:
    //这种合并每帧都需要重新计算批次(即将满足条件的对象进行批处理),所以叫动态

    //说人话
    //动态批处理是一种Unity在运行时
    //由 CPU 自动将满足限制条件的多个动态物体网格合并成一个大网格
    //一次性提交给 GPU,从而减少 Draw Call 的技术

    //注意:
    //1.动态批处理是内置渲染管线中的重要优化手段
    //  SRP(URP/HDRP)管线中主要采用SRP Batcher(我们之后会讲解)
    //2.Unity中创建了一个对象,但是DrawCall并不是增长了1次,而是n次
    //  是因为阴影、深度等贴图以及多Pass、透明、子网格、多材质都会增加DrawCall的次数

    #endregion

    #region 知识点二 如何开启动态批处理

    //内置渲染管线:
    //Player Settings ——> Other Settings ——> Rendering ——> Dynamic Batching

    //URP管线:
    //URP管线,主要采用的是之后要讲解的SRP Batcher优化方案
    //在较新版本中(Unity2023之后)
    //URP管线中已不再怎么使用动态批处理
    //如果想要使用,需要切换为内置渲染管线

    #endregion

    #region 知识点三 动态批处理的限制

    //1.必须满足的硬性条件
    //  顶点 <= 225 或 300  Unity2021之前为300
    //  顶点 * 顶点属性 <= 900
    //  即
    //  每个网格最多 225或300 个顶点
    //  并且着色器使用的顶点属性不能超过900
    //  顶点属性是在Shader中会用来进行渲染计算的数据
    //  常用属性有 顶点的 位置、法线、切线、多套UV纹理、顶点颜色、骨骼权重、骨骼索引等等

    //  对于简单Shader来说 一般至少需要 位置、法线、UV 3个属性
    //  225 * 3 = 675 
    //  300 * 3 = 900
    //  此时顶点满足条件,顶点*顶点属性也满足条件
    //  会进行动态批处理

    //  若对于一些复杂Shader,假设使用 5 个 属性
    //  那么网格的顶点上限 就是 900 / 5 = 180 个顶点

    //  只要超出225(Unity2021之前为300)顶点或900属性任意一个限制
    //  该网格将不会被动态批处理

    //2.相同材质实例
    //  不同对象之间,必须使用同一个Material对象(同一内存引用)
    //  并且材质属性、Shader关键字、渲染状态必须完全一致

    //3.不能是SkinnedMeshRenderer(蒙皮网格渲染器)
    //  一般骨骼动画模型会使用蒙皮网格渲染器,因为每帧 CPU 都要做蒙皮变形,数据不适合直接合并
    //  只有MeshRenderer(网格渲染器)和ParticleSystem(粒子系统)能进行动态批处理

    //4.对象不能再变换中包含镜像
    //  即一个正比例缩放的游戏对象和一个负比例缩放的游戏对象不能放在一起批处理

    //5.相同光照状态
    //  如果使用了光照烘焙中的光照贴图,那必须保持使用的是同一个光照贴图
    //  并且 光照探针、反射探针、雾效开关等必须一致,否则渲染状态切换会打断批处理。

    //6.同一渲染队列
    //  渲染队列(Render Queue)必须一致,比如都在 不透明 队列,否则会被排序拆开

    //7.材质不能开启 GPU Instancing
    //  开启了 Enable GPU Instancing 会让 Unity 用 Instancing 路径,而不是动态批处理(两者互斥)

    //8.Shader最好单Pass
    //  否则每个Pass都有单独的DrawCall

    //等等

    #endregion

    #region 知识点四 适合使用动态批处理的情况

    //大量使用相同材质的小型动态网格,并且满足批处理条件,且顶点数低于 225(300)/900 属性限制
    //关键点:
    //物体是动态的,位置、旋转、缩放会发生改变,不能用静态批处理时,首选动态批处理
    //使用场景:
    //1.游戏中的小道具、小物件
    //  比如 地图上的金币、宝石、道具箱子、掉落物
    //2.简单的动态环境元素
    //  比如 旋转的风扇叶片等
    //3.低顶点数的动态建筑元素
    //  小路灯、小栅栏、路标等(材质相同、顶点少)
    //4.粒子系统的网格粒子(Mesh Particle)
    //  多个相同网格的小粒子,如果没用 GPU Instancing,也能走动态批处理
    //等等

    //不太适合使用动态批处理的情况
    //1.网格顶点数大(超 225(300) 或超 900 属性限制)
    //2.多 Pass Shader(描边、ForwardAdd 多光源、阴影等)
    //3.材质多样化(不同材质实例会切断批处理)
    //4.Skinned Mesh(角色、骨骼动画模型)
    //5.URP/HDRP 新版本,优先用 SRP Batcher + GPU Instancing

    #endregion
}

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏