如灰尘保佑的礼物

Unity Static Batching

批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。但这是建立在GPU相对空闲,而CPU把更多的时间都耗费在渲染命令的提交上时,才有意义。

如果瓶颈在GPU,比如GPU性能偏差,或片段着色器过于复杂等,那么没准适当减少批处理,反而能达到优化的效果。

静态合批可以简单的分为预处理阶段的合并,和运行阶段的批处理

合并阶段

  • 将设置了静态处理标记的网格收集起来,对网格的顶点空间变换到合并根结点的坐标系下,再合并成一个新的网格,新网格是以若干个子网格的形式组合而成。
空间变换的目的是,如果需要对合并后的对象进行空间变换,无需修改缓冲区内的顶点属性,只需要做一次根节点的矩阵变换。
  • 不同平台对于合并是有顶点和索引数量限制的,超过此限制则会合并成多个新网格。

  • 打包时的自动合并会膨胀场景文件,会在一定程度上影响场景的加载时间。

  • 运行时可以调用StaticBatchingUtility.Combine(root)进行静态合批。

所有对象使用相同材质的情况下,有几个测试用例:
1. 场景内所有对象都在root节点下,合批一次。
2. 场景内所有对象都在root节点下,且部分子节点标记了static,合批一次。
3. root节点外存在其他对象标记了static,且部分子节点标记了static,
标记static的对象进行一次合批,root下的其他子节点进行一次合批。
  • 如果合批的对象网格不同、材质不同Unity也会将它们的网格进行合并。

批处理阶段

  • 如果手动替换过静态合批对象的Material,会打断该对象的合批处理。
  • 如果手动替换过静态合批对象的网格,也会打断该对象的合批处理。
  • 动态加载并实例化一个带静态标记的GameObject到场景中,是不会被当做静态合批处理的。
  • 自动静态合批的根节点在场景上,因此无法对其进行空间变换,而手动静态合批,因为根节点不是场景而是一个游戏对象,所以可以通过修改根节点的空间属性(位置、大小及缩放值),达到诸如移动整个合批单位的目的。
  • Batching合批不等于DrawCall,一次静态合批,如部分物体被设置为隐藏或被视椎体剔除可能会有多次DrawCall,但是引擎会忽略这些DrawCall将其统计为一次合批。
  • 静态合批包含多个材质时,引擎会将材质分组,这些材质会使用相同的顶点,索引缓冲区。

为什么不直接使用大网格?

  • 静态合批可以主动隐藏部分对象。
  • 静态合批可以有效参与CPU的视锥剔除。

静态合批的利弊

  • 运行时顶点、索引信息不会发生变化,无需CPU消耗算力维护。
  • 渲染前进行视锥体剔除,减少了顶点着色器对不可见顶点的处理次数,提高了GPU的效率。
  • 合批后的网格会常驻内存,在有些场景下可能并不适用。