Unity性能优化(DrawCall合批)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zhaixh_89/article/details/100153577

为了在屏幕上绘制一个对象,游戏引擎必须发出一个绘制命令给图形化API接口(如OpenGL 或者Direct3D),DrawCalls通常都是资源密集的,图形化API每一次DrawCall都要做很多重要的工作,
会造成CPU一定的性能开销,这通常是由于在显示卡驱动中DrawCall资源密集验证和转化步骤之间状态切换导致的
Unity利用两种技术来解决上述问题:

Dynamic batching
对于足够小的网格, 动态合批将转换他们在CPU上的顶点, 将许多相似的顶点分成一组,一次性把他们全部渲染出来
Static batching
对于标记为Static的对象(前提是不能移动)合并成一个大的网格,以更快的方式渲染出来
内置的合批比起手动合并对象有更多好处,最显著的就是对象仍然可以被单独的剔除(culled ),但是它们也有一些不足之处,静态合批会增加内存开销,动态合批会增加CPU开销

Material set-up for batching
只有当对象共享相同的材质时才能实现合批,因此,如果你想较好的实现合批应该专注于尽可能让更多的游戏对象共享材质
如果有两个仅纹理不同其他都相同的材质,可以把这两个纹理合并一张大的纹理,这个过程通常被称为纹理图集(可参阅 Wikipedia page on Texture atlases for more information)
一旦纹理存在于相同的图集中,便可以用一个材质来替代

如果需要从脚本中访问共享材质属性,要注意使用Renderer.material会造成材质的拷贝,而使用Renderer.sharedMaterial来调用则不会拷贝材质。
阴影投射在渲染时只要材质中所需的阴影通道的值是相同的, 即使所使用的材质不同也会实现动态合批,比如组多箱子可以使用拥有不同纹理的材质,但是对于阴影投射渲染来说是不相关的,
因此在这种情况下也可以实现合批

Dynamic batching (Meshes)

1、如果一些对象拥有相同的材质并且满足其他条件,unity会自动分批次将这些对象移动到相同的DrawCall命令中处理,动态合批是引擎自动处理的,不需要开发者做额外的操作
动态合批的对象在每一个顶点上都有一定的性能开销,因此合批只适用于包含不超过900个顶点属性和不超过300个顶点的网格。如果shader使用了顶点位置,法线和UV那么仅支持低于300顶点的mesh,
而如果shader使用了顶点位置,法线、UV0、UV1和切向量,则之多仅支持180顶点
注意:属性数量的限制在今后可能会改变
2、如果两个对象在transtorm上包含镜像(例如,GameObject A with +1 scale和GameObject B with -1 scale不能一起批处理)
3、使用不同的材质资源,即使在本质上他们是相同的,将导致对象无法合批,唯一例外的是阴影投射渲染
4、拥有光照映射的对象有更多的渲染参数:光照映射索引、偏移和缩放。通常,动态光映射游戏对象应该指向完全相同的光映映射位置进行批处理。
5、多通道shader会打断合批
几乎所有的unity shader在前向渲染中都支持多个光照,有效的为他们实现额外的通道,针对“additional per-pixel lights绘制不会合批
遗留的延迟渲染路径禁用动态合批,因为它必须渲染对象两次

动态批处理通过CPU是将所有GameObject顶点转换到世界空间,因此只有开销小于draw调用时才有优势。draw调用的资源需求取决于许多因素,主要是使用的图形API。例如,在控制台或像Apple Metal这样的现代api上,
draw调用开销通常要低得多,而且通常动态批处理根本不是优势。

Dynamic batching (Particle Systems, Line Renderers, Trail Renderers)

对于unity动态生成的几何组件,动态合批的工作方式与它在网格的工作方式不同
1、对于每个兼容的渲染器类型,Unity将所有可批处理的内容构建到一个大的顶点缓冲区中
2、渲染器为批处理设置材质状态。
3、Unity将顶点缓冲区绑定到图形设备。
4、对于合批中的每个渲染器,Unity会把偏移量更新到顶点缓冲区,然后提交一个新的drawcall。

当测量图形设备调用的成本时,渲染组件最慢的部分是设置材质状态。相比之下,将不同偏移量的绘制调用提交到共享顶点缓冲区中非常快。
这种方法非常类似于Unity在使用静态批处理时提交draw调用的方式

Static batching

静态批处理允许引擎减少对任何尺寸的几何图形的绘制调用,前提是它共享相同的材质,并且不能移动。它通常比动态批处理更有效(它不转换CPU上的顶点),但是它使用更多的内存。
在游戏中只有明确指定为static并且不能移动、旋转、缩放的对象才可以使用静态合批,只需在Inspector面板中勾选Static选项如下图:

使用静态合批为了存储后并的网格会占用更多的内存,如果多个对象在静态合批之前共享相同的网格,在编辑器或者运行时为每个对象创建网格副本
,因此静态合批未必总是有益于性能提升的,有时为了避免静态合批占用更多内存我们不得不牺牲渲染性能,例如在浓密的森林将树标记为static将对内存产生严重的影响

在引擎内部,静态合批的机制就是将游戏对象转换到世界空间下并为他们创建一个共享的顶点和索引缓冲区,如果你启用了Optimized Mesh__ Data__ (in the Player settings)
,那么unity会删除那些在创建顶点缓冲区时不被shader所使用的顶点元素,有一些特定的关键字会检查实现它,比如,如果unity没有检测到LIGHTMAP_ON 关键字,它将从合批中移除lightmap(光线映射)uv,
然后,对于同一批次的可见对象,unity将执行一系列简单的DrawCall,每次调用之间几乎没有状态变化,从技术上来说,unity并不保存API DrawCall,而是保存它们之间的状态更改(资源密集型部分),
大多数平台上合批的限制时64k个顶点和64k个索引(OpenGLES上的48k个索引,macOS上的32k个索引)。

注意

目前只有网格渲染器、拖尾渲染器、线渲染器、粒子和精灵渲染器可以被合批,这意味着蒙皮网格、布和其他类型的渲染组件不会合批。
合批只会发生在渲染类型相同的对象之间
半透明着色器通常需要游戏对象以从后往前的顺序渲染,unity会首选按照这个顺序对对象进行排序, 然后尝试对他们进行合批,但是由于必须严格满足顺序,因此相比不透明物体可实现的合批更少。
手动组合彼此接近的GameObjects可能是绘制调用批处理的一个非常好的替代方法。例如,一个有很多抽屉的静态橱柜通常可以合并成一个网格,无论是在3D建模应用程序中还是使用Mesh.combinemeshes。

猜你喜欢

转载自blog.csdn.net/zhaixh_89/article/details/100153577