批量渲染是通过减少CPU向GPU发送渲染命令(DrawCall)的次数,以及减少GPU切换渲染状态的次数,尽量让GPU一次多做一些事情,来提升逻辑线和渲染线的整体效率。
Draw Call性能消耗原因是命令从Runtime到Driver的过程中,CPU要发生从用户模式到内核模式的切换。
静态合批
静态合批也叫静态批处理,是Unity的一种优化技术。
对于始终静止不动的物体使用静态合批后,CPU会把它们合并为一个批次发送给GPU处理,这样可以减少Draw Call带来的性能消耗,从而提升游戏性能。
要使用静态合批,必须确保Edit——Project Settings——Player——Other Settings——Static Batching是勾选的。
把一个物体设置为静态的方法:
选中该物体,点击在Inspector窗口右上角的Static右方的下拉菜单,选择Batching Static。
使用静态合批虽然可以提升游戏性能,但是设置为静态的物体在整个游戏中就不能再运动了,强行使它们运动会出问题。
而且即使按照以上步骤进行了静态合批,也不一定保证会成功,必须满足以下全部条件,静态合批才会成功:
- 1、游戏对象处于激活状态。
- 2、游戏对象有一个Mesh Filter组件,并且该组件已启用。
- 3、Mesh Filter组件具有对网格的引用。
- 4、网格已启用Read/Write功能。
- 5、网格的顶点计数大于0。
- 6、该网格尚未与另一个网格组合。
- 7、游戏对象有一个Mesh Renderer组件,并且该组件已启用。
- 8、网格渲染器组件不将任何材质与DisableBatching标记设置为true的着色器一起使用。
- 9、要批处理在一起的网格使用相同的顶点属性。例如,Unity可以将使用顶点位置、顶点法线和一个UV的网格与另一个UV进行批处理,但不能将使用顶点定位、顶点法线、UV0、UV1和顶点切线的网格进行批处理。
即使静态合批成功,合出来的每个批次可以包含的网格顶点数是有限的,最多是64000个顶点。如果超过这个数,则会创建到另一个批次中。
如果要在游戏运行时进行静态合批,则可以使用StaticBatchingUtility类的Combine方法。
StaticBatchingUtility.Combine(GameObject 根物体)
对指定的根物体的所有子孙物体进行静态合批。
只有当它们符合静态合批的所有条件,静态合批才会成功。
成功之后,这些物体就不能再运动了,强行运动会出问题。但是该根物体仍然允许运动。
动态合批
动态合批也叫动态批处理,是Unity的一种优化技术。
对移动的物体使用动态合批后,则Unity不会一个个绘制它们,而是把它们合并为一个批次(Batch),再由CPU把它们一次性提交给GPU进行处理,这样可以减少Draw Call带来的性能消耗,从而提高性能。
动态合批默认是由Unity自动完成。可以在Edit——Project Settings——Player——Other Settings——Dynamic Batching查看。默认Dynamic Batching是勾选的,当条件满足时,Unity会自动对使用了相同材质(Material)的物体进行动态合批。如果取消勾选,则不会进行动态合批。
即使勾选了Dynamic Batching,也必须同时满足以下条件,动态合批才会成功:
- 1、Unity不能对包含超过900个顶点属性和225个顶点的网格应用动态批处理。这是因为网格的动态批处理对每个顶点都有开销。例如,如果你的着色器使用顶点位置、顶点法线和单个UV,那么Unity最多可以批处理225个顶点。然而,如果你的着色器使用顶点位置、顶点法线、UV0、UV1和顶点切线,那么Unity只能批处理180个顶点。
- 2、如果GameObjects使用不同的材质实例,Unity就不能将它们批处理在一起,即使它们本质上是相同的。唯一的例外是阴影施法者的渲染。
- 3、带有光贴图的游戏对象有额外的渲染参数。这意味着,如果你想批处理光照贴图的游戏对象,它们必须指向相同的光照贴图位置。
- 4、Unity不能完全将动态批处理应用于使用多通道着色器的GameObjects。
几乎所有的Unity着色器都支持正向渲染中的多个光源。为了实现这一点,他们为每个光处理一个额外的渲染通道。Unity只批处理第一个渲染通道。它不能批处理额外的逐像素灯光的绘制调用。
遗留延迟渲染路径不支持动态批处理,因为它在两个渲染通道中绘制GameObjects。第一个通道是灯光预通道,第二个通道渲染GameObjects。
其中我们要注意的是,物体必须使用相同的材质,才有可能成功进行动态合批。
使用动态合批往往能减少CPU和GPU的开销,提升游戏性能,但同时也会占用一定的内存。
是否要开启动态合批,要根据自己的项目来定。可以尝试启用,在性能分析器中看看效果如果,如果效果好,再确定启用它。
GPU Instancing
使用GPU Instancing可以在一个Draw Call中同时渲染多个相同或类似的物体,从而减少CPU和GPU的开销。
要启用GPU Instancing,我们可以选中一个材质,然后在Inspector窗口勾选Enable GPU Instancing,这样就可以了。
但是即使勾选了Enable GPU Instancing,也不一定会成功。
要成功使用GPU Instancing进行优化,游戏对象必须同时满足以下条件:
- 1、使用相同的材质和网格。
- 2、材质的着色器必须支持GPU Instancing。例如标准着色器和表面着色器就支持GPU Instancing。
- 3、网格的顶点布局和着色器必须相同。如果网格的顶点布局或着色器不同,那么它们就无法被合并成一个实例。
- 4、每个实例需要有不同的变换信息(例如位置、旋转、缩放)。虽然多个实例可以使用相同的材质和网格,但是它们必须拥有不同的变换信息才能被正确地实例化并渲染出来。
另外需要注意的是,GPU Instancing与SRP Batcher不兼容。如果项目使用了SRP Batcher,并且配置为优先使用SRP Batcher而不是GPU实例化,启用GPU实例化可能不会生效。SRP Batcher是Unity提供的一种渲染优化技术,它可以将多个网格合并成单个批次进行渲染,从而提高性能。在这种情况下,GPU实例化将被忽略。
使用GPU Instancing往往能减少CPU和GPU的开销,提升游戏性能,但同时也会占用一定的内存。
一般来说,当场景中有大量重复的网格实例时,可以尝试启用GPU Instancing。例如场景中有大量树木、草地、石块等,这些实例具有相同的网格和材质,只是位置、颜色等属性稍有差异,那么启用GPU Instancing或许能够显著提高性能。
遮挡剔除
正常情况下,如果一个障碍物A挡住了后面的物体B,虽然我们看不见物体B,但是Unity仍然会消耗性能来渲染这个物体B。这样CPU和GPU就会有一部分性能白白浪费在渲染物体B身上。
如果想在一个障碍物挡住了后面的物体后,不渲染被挡住的物体,则可以使用遮挡剔除。
以一堵墙挡住几个小球为例,选中这堵墙,在Inspector窗口右上角的Static右侧的下拉菜单处选择Occluder Static,则这堵墙就是遮挡物。分别选中这些小球,在Inspector窗口右上角的Static右侧的下拉菜单处选择Occlu