为了提高游戏性能,降低游戏运行时的DrawCall,Unity会对网格顶点进行批处理,而批处理又分成了动态批处理和静态批处理
动态批处理
优点:不用自己做任何事情,Unity会在游戏中自动进行动态批处理,只要满足下述条件。
在Unity中,要进行动态批处理需要满足以下条件:
- 顶点属性要小于900。例如,如果shader中需要使用顶点位置、法线和纹理坐标这三个顶点属性,那么要想让模型能够被动态批处理,它的顶点数目不能超过300。因此,优化策略就是shader的优化,少使用顶点属性,或者模型顶点数要尽可能少。(这个是《UnityShader入门精要》这本书上说到的,同时书上也说了不一定是900,可能不同版本的Unity会有所区别,这个可以自己在Unity中去手动验证得出)
- 多Pass的shader会中断批处理。
- 使用LightingMap的物体需要小心处理。为了让这些物体可以被动态批处理,需要保证它们指向LightingMap中的同一位置。
原理:Unity会检测哪些GameObject使用了同一个共享材质,然后去合并这些使用了同一个共享材质的网格顶点数据,形成一个新的大网格,然后传给显存,直接渲染这个大网格就相当于渲染了所有的被合并的小网格,而这只需要一次DrawCall。
为什么用了不同的材质,不同的贴图,不同的材质属性等会导致不能动态批处理呢?
在Unity渲染其实也是调用的OpenGL或者是DirectX,因为我只了解过OpenGL,所以以OpenGL为例(DirectX也是同理)。OpenGL中要渲染一个东西出来的,需要顶点着色器和片元着色器,这个对应的是ShaderLab中的顶点着色器和片元着色器,然后还需要把要渲染的网格的顶点属性作为一个数组绑定到VBO(顶点缓存对象)中去,然后绑定VAO(顶点数组对象)并设置顶点数组的属性,然后一些需要外部设置的着色器属性也是在这个阶段进行设置,当做完这些之后,再调用glDrawElements(也就是一次DrawCall)去渲染这个物体。这就是OpenGL渲染一个东西的最简单的流程。所以回到问题,为什么贴图,材质属性等必须一样呢?因为如果不一样的话,他们就不能通过一次DrawCall去设置这些属性和贴图了,想想看,如果A物体使用了贴图A,B物体使用了贴图B,如果把他们合并成一个大网格,要直接在一个DrawCall里渲染出来的话,这个合并好的大网格到底该用贴图A还是贴图B呢?无论使用哪个,都是不对的。所以A物体和B物体不能进行批处理。
静态批处理
需要做的事情:把要进行静态批处理的GameObject在Inspector面板右上角的Static勾选(实际上只需要勾选Batching Static即可)
优点:因为只需要进行一次,所以性能会比动态批处理要好。
缺点:进行了静态批处理之后的GameObject不能在游戏运行时改变位置或者是跟渲染有关的属性。并且因为把所有要静态批处理的GameObject都合并成一个大网格保存起来,所以这实际上相当于即时是同一个GameObject,也需要复制一份网格数据一起保存在这个大网格的顶点数据里面去,这样就导致了占用的内存变多了。
原理:在开始阶段把需要静态批处理的GameObject进行一次网格合并操作,然后把这个合并之后的大网格保存起来,后续都是用这个网格而不需要再进行合并。