一、绘制原理

为了将物体绘制到屏幕上,引擎必须向图像API(例如OpenGL、Direct3D)发送一个DrawCall指令,每一次发送DrawCall指令的过程为一个渲染批次(Batch),而这个过程分为两大部分:设置渲染状态(setPass)和调用DrawCall(Batches),其中设置渲染状态属于比较重的分工,对于加载到游戏中的资源和对象等,CPU需要计算其顶点相关的矩阵,渲染所用的贴图,渲染所用到的材质和shader,渲染所用到的灯光等,如果每个物体的材质和贴图等都不一样,此时CPU的主要工作就是设置这些物体的渲染状态后调用DrawCall,从而造成CPU性能的开销,简单来说也就是CPU向GPU发送指令并由GPU进行绘制,即CPU与GPU进行一次数据交换就是一个DrawCall。

二、批处理

因为每一次发送DrawCall指令都会造成CPU的性能开销,而CPU的处理速度比GPU慢多了,所以可以将绘制的压力移交给GPU
Unity可以将一些物体进行合并,从而用一次DrawCall来渲染他们。这一操作,称为批处理,批处理能得到更好的渲染性能

例如渲染一千个三角形,如果把它们按一千个单独的网格进行渲染则需要请求1000次DrawCall,而如果直接渲染一个包含了一千个三角形的网格则只需要请求1次DrawCall,这样减轻了CPU的开销在性能上会有很大的提升

Unity中有两种批处理方式:一种是动态批处理,一种是静态批处理。对于动态批处理来说,好消息是一切处理都是自动的,不需要我们自己做任何操作,而且物体是可以移动的,但坏消息是,限制很多,可能一不小心我们就会破坏了这种机制,导致Unity无法批处理一些使用了相同材质的物体。对于静态批处理来说,好消息是自由度很高,限制很少,坏消息是可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了。

三、渲染统计窗口

unity优化打开动态批处理 unity 动态批处理_批处理

  • Batches:相当于DrawCall次数
  • Saved by batching:通过批处理节省的DrawCall次数
  • SetPass calls:设置渲染状态的次数

四、动态批处理

动态批处理默认是关闭的,需要手动开启:Project Setting—Player—勾上Dynamic Batching

unity优化打开动态批处理 unity 动态批处理_unity_02

  1. 动态批处理仅支持顶点数小于900的网格物体。
  2. 如果Shader使用了顶点位置、法线、UV值三种属性,则只能动态批处理300个顶点以下的物体。如果Shader使用了顶点位置、法线、UV0、UV1和切向量,那么只能动态批处理180个顶点以下的物体。
  3. 不要使用缩放,分别有缩放(1,1,1)和(2,2,2)的两个物体将不会进行批处理。 统一缩放的物体不会与非统一缩放的物体进行批处理。使用缩放比例(1,1,1)和(1,2,1)的两个物体将不会进行批处理,但使用缩放尺度(1,2,1)和(1,3,1)的两个物体可以进行批处理。
  4. 使用不同材质的实例化物体将会导致批处理失败。
  5. 拥有lightmap的物体不会进行批处理,除非他们指向lightmap的同一部分。
  6. 多通道的Shader会妨碍批处理。因为几乎Unity中所有的着色器都在前向渲染中支持多个光源,并为其有效开辟多个通道。
  7. 预制体的实例化会自动使用相同的网格模型和材质

五、静态批处理

静态批处理需要在Inspector面板上勾选上Static

unity优化打开动态批处理 unity 动态批处理_unity优化打开动态批处理_03

  1. 只要勾选上Static并拥有相同材质的物体即可在运行时进行静态批处理
  2. 但是静态批处理会为每一个物体创建一个合并后的Combined Mesh,需要额外的内存空间来存储合并后的Combined Mesh,所以使用时要慎用(例如一片浓密的草地,如果使用静态批处理则会造成严重的内存开销)

六、总结

  1. 静态批处理和动态批处理都能降低DrawCall,最主要的前提是物体使用同一个材质
  2. 静态批处理适用于场景中位置不会改变并且单对象不能太多的物体,像树林,草地这种用动态批处理更适合
  3. 动态批处理限制较多,顶点数要求,材质要求等等。静态批处理限制较少,是用内存换性能的方法,具体情况具体分析
  4. 虽然批处理是个很好的方式,但很容易就打破它的规定。例如,场景中的物体都使用Diffuse材质,但它们可能会使用不同的纹理。因此,尽可能把多张小纹理合并到一张大纹理(Atlas)中是一个好主意。