游戏引擎中的合批技术(Batching)是一种优化渲染性能的关键手段,其核心目标是减少绘制调用(Draw Call)次数,从而降低CPU与GPU之间的通信开销,提升帧率。
合批技术的基本原理
-
Draw Call的瓶颈
每次绘制一个物体时,CPU需要向GPU发送材质、网格、变换矩阵等数据,这个过程称为Draw Call。过多的Draw Call会导致CPU过载,成为性能瓶颈。 -
合批的核心思想
将多个物体的渲染数据合并为一个或少量Draw Call,减少CPU与GPU之间的通信次数。合批成功的关键在于共享相同的渲染状态(如材质、纹理、Shader等)。 -
合批类型
- 静态合批(Static Batching):合并不移动的物体(如场景中的静态建筑)。
- 动态合批(Dynamic Batching):合并动态物体(如可移动的物体或材质参数需要经常变化的物体),但需要满足特定条件(如相同材质、顶点数限制)。
- GPU Instancing:通过一次Draw Call渲染多个相同网格的实例,适用于大量重复物体(如草地、人群)。是一种更高级的动态合批。
静态合批(Static Batching)与动态合批(Dynamic Batching)的区别
特性 | 静态合批 | 动态合批 |
---|---|---|
适用对象 | 不移动、不变化的静态物体(如建筑、地形、固定装饰)。 | 动态物体(如可移动、旋转、缩放的物体,或参数变化的实例)。 |
合批时机 | 在编辑器或烘焙阶段预先合并(离线处理)。 | 在运行时动态合并(实时处理)。 |
性能开销 | 无运行时开销,合并后渲染效率极高。 | 运行时需实时合并数据,可能增加CPU开销(需满足严格条件)。 |
灵活性 | 合并后物体无法单独控制(如移动、隐藏)。 | 保留实例独立性,支持动态修改参数。 |
内存占用 | 合并后的网格占用更多内存(存储合并后的顶点数据)。 | 内存占用较低(仅存储差异化参数)。 |
适用场景 | 大规模静态场景(如城市、森林)。 | 少量动态物体(如可破坏的箱子、动态植被)。 |
合批条件 | 物体必须标记为静态(Static),使用相同材质和网格。 | 需满足顶点数限制、相同材质、无复杂变换差异等条件。 |
Unreal Engine(UE)中的静态合批实现方法
UE主要通过以下技术实现静态合批:
1. HLOD(Hierarchical LOD)
- 原理:将远距离的多个静态物体合并为简化的代理网格(Proxy Mesh),也就是将多个不同的Actor烘焙成一个SM,减少Draw Call。
- 操作步骤:
- 在编辑器中使用 HLOD Outliner 创建HLOD集群。
- 生成代理网格(支持自动或手动调整LOD层级)。
- 运行时根据摄像机距离切换原始模型与代理模型。
- 适用场景:开放世界场景中优化远处物体渲染。
2. 手动合并Actor(Merge Actors)
- 原理:将多个静态Actor合并为单个静态网格体,合并过程中可以选择简化材质数量以达到减少Draw Call的目的。底层原理和HLOD相同。
- 操作步骤:
- 在编辑器中选择多个静态Actor。
- 通过 Tools > Merge Actors 生成合并后的网格。
- 注意事项:
- 合并后无法单独控制子物体(如动态交互、光照烘焙需重新处理)。
- 合并后的网格可能增加内存占用。
3. 自动实例化(Auto Instancing)
- 原理:自动检测场景中相同静态网格和材质的物体,合并为单个Draw Call。
- 条件:
- 物体标记为 Static。
- 使用相同的 Static Mesh 和 Material Instance。
- 优化效果:适合处理大量重复静态物体(如路灯、石块)。
- 注意事项:
- 此方法很笨,必须是相同的SM和相同的MI资产。如果MI复制多份分给长江中的相同SM,复制的MI参数不变,也会合批失败。
Unreal Engine(UE)中的动态合批实现方法
UE对动态合批的支持相对有限(主要依赖GPU Instancing),但可通过以下方式实现类似效果:
1. ISM(Instanced Staic Mesh)
- 原理:通过GPU Instancing实现,每个实例的可变参数统一打包在一个Buffer中,渲染时按照实例ID去访问可变参数。
- 实现步骤:
- 在材质中启用 Use with Instancing。
- 在场景中创建Instanced Staic Mesh Component
- 使用 PerInstanceCustomData 传递差异化参数(如颜色、缩放)。
- 在蓝图或C++中通过
SetCustomPrimitiveData
设置实例数据。
- 适用场景:动态物体(如建筑、人群、车辆)。
2. CPD(Custom Primitive Data)
- 原理:与ISM底层原理一致,只是一套不同的封装
- 实现步骤:
- 在母材质中需要Instance间可变的参数勾选 Use Custom Primitive Data。
- 在场景中创建Static Mesh Component
- 在蓝图或C++中通过
SetCustomData
设置实例数据。
- 适用场景:与ISM一致。
3. HISM(Hierarchical Instanced Staic Mesh)
- 原理:在ISM的基础上对LOD管理进行的优化。ISM每个Instance独立计算LOD,放在如草地等植被上会开销巨大。HISM将场景中的实例按空间划分为层级结构(如四叉树或八叉树),实现高效剔除和切换LOD。
- 实现步骤:
- 在Actor蓝图中,通过
Add Component
选择 Hierarchical Instanced Static Mesh。 - 在HISM组件的Details面板中,设置 Static Mesh 属性为需要实例化的网格(如树木、岩石)。
- 在HISM组件的Details面板中,点击 Add Instance 按钮,在场景中直接拖拽放置实例,或在蓝图中使用
Add Instance
节点动态添加实例,支持设置位置(Location)、旋转(Rotation)、缩放(Scale)。
- 在Actor蓝图中,通过
- 适用场景:植被系统。
特性 | HISM | ISM |
---|---|---|
剔除效率 | 分层结构优化,支持高效视锥/遮挡剔除 | 仅基础视锥剔除 |
LOD支持 | 自动应用网格LOD,支持动态切换 | 无内置LOD管理,需手动处理 |
适用场景 | 超大规模静态物体(>1000实例) | 中小规模动态物体(<1000实例) |
性能开销 | 层级维护有额外CPU开销 | 轻量级,适合频繁更新的实例 |
4. Niagara粒子系统(GPU粒子)
- 原理:粒子系统默认使用GPU Instancing,将数万粒子合并为单个Draw Call。
- 优势:支持动态参数(如位置、速度、颜色)实时变化。
- 示例:火焰、雨雪、子弹轨迹。
5. 动画实例化(Animation Instancing)
- 插件或自定义实现:通过共享骨骼动画数据,批量渲染动画模型(如人群)。
- 插件推荐:
- Animation Budget Allocator(UE官方实验性插件)。
- Mass AI(大规模人群渲染框架)。
性能注意事项
- 静态合批的代价:
- 合并后的网格可能导致光照烘焙复杂化(需重新展开UV)。
- 内存占用增加(合并后的顶点/索引数据)。
- 动态合批的限制:
- GPU Instancing的顶点数量有上限,每个硬件平台可能不同,需要注意。
- 频繁修改实例参数可能导致CPU开销上升。
- 调试工具:
- 使用
stat scenerendering
查看Draw Call数量。 - 使用图形调试工具(如RenderDoc)观察Draw Call是否合并
- 使用