一、我们为什么要做DrawCall批处理
什么是drawcall,当我们需要绘制一个物体时,就需要调用底层的图形接口,如OpenGL,DX等;也就是在CPU收集完物体的数据信息后向GPU发起调用的一个过程,过多的drawcall就会造成CPU的性能瓶颈;
Unity提供了两种技术来处理
- 动态批处理:对足够小的网格,它在CPU层自动转换它们的坐标,将许多顶点组织到一起然后一次绘制;
- 静态批处理:合并静态也就是不需要移动的游戏对象,合并成一个大的网格,然后快速绘制;
两种批处理的方式都各有负面影响,动态批处理使用不当可能会引起CPU瓶颈,而静态批处理则会造成内存性能问题;
二、批处理中的材质设置
只有当游戏对象拥有相同的材质时,他们才有可能进行合批处理,所以,如果想要进行合批,就需要在尽可能多的游戏对象中共享材质;
如果有两个相同的材质,但是它们纹理不同,这时可以通过合并纹理的方式来解决;一旦纹理在同一个图集中,就可以使用单个材质来完成;
如果需要从脚本中来获取材质信息,那么一定要使用Renderer.sharedMaterial,因为Renderer.material会创建材质的拷贝;
三、动态批处理
Unity可以用同一个drawcall自动批处理移动的游戏对象,前提是它们材质相同并满足一定规范;动态批处理是自动完成的;
- 合批动态游戏对象需要顶点有限制,网格不得多于900个顶点属性,不得多于300个顶点;如果shader使用了顶点坐标,法线以及UV,则顶点上线为300,如果包含了UV0,UV1和切线等,顶点上限为180;
- 如果两个游戏对象的transform里包含镜像,则不能合批;
- 多pass的shader会打断合批
动态批处理是通过将所有的游戏对象在CPU转换到世界空间下,所以只有当这个转换到代价低于drawcall的代价时,它才是有利的;也就是它为了将游戏对象合批,把渲染管线中几何阶段的MVP变换的Model变换从GPU移到CPU,所以会给CPU带来一定的消耗;
Dynamic batching(),Particle Systems,Line Renderers,Trail Renderers
- 对每个兼容的类型,unity会将所有批处理的内容放置到一个大的顶点缓冲区
- 然后renderer会为合批设置材质状态
- unity将会绑定顶点缓冲区到图形设备
- 对每个batch中的renderer,unity会将偏移更新到Vertex Buffer,然后提交一个新的drawcall
四、静态批处理
静态批处理允许引擎去减少任何大小的几何体的drawcall,只要它共用相同的材质而且不移动;它比静态批处理更高效,因为它不在CPU层做顶点的变换,但是它使用更多的内存;
为了使用静态批处理,我们需要特意标记某个对象是静态的,而且是不能移动的、旋转和缩放的;
但是使用静态批处理要求额外的内存消耗来存储合并的几何体,它的原理是通过转换静态对象到世界空间中,然后建立一个共享的顶点和索引缓冲;
五、其它
1.半透明物体的合批
半透明物体要求对象是从后向前进行渲染的,unity首先会按照顺序对游戏对象进行排序,然后尝试合批,因为需要严格满足渲染顺序,所以半透明物体通常要比不透明物体少很多合批;
2.SpriteRenderer
为什么一个SpriteRenderer材质可以赋值不同的纹理,我们可能有n个不同的spriterenderer对象,有不同的sprite,但是材质只有一个?
MaterialPropertyBlock is used to modify these values
在Sprite Renderer组件下,我们有时会看到如上提示,原因是Sprite Renderer的材质使用了MaterialPropertyBlock去给材质赋予纹理,默认的sprite shader会隐藏_MainTex属性,所以在材质上看不到纹理的槽位;
参考:Unity-Forums:Why do I have "MaterialPropertyBlock is used to modify these values" on all materials?