unity中的渲染优化技术——(减少draw call数目 方法一:动态批处理)

批处理的实现原理就是为了减少每一帧需要的draw call数目。为了把一个对象渲染到屏幕上,CPU需要检查哪些光源影响了该物体,绑定shader并设置它的参数,再把渲染命令发送给GPU。当场景中包含了大量对象时,这些操作就会非常耗时。一个极端的例子是,如果我们需要渲染一千个三角形,把他们按一千个单独的网格进行渲染所花费的时间要远远大于渲染一个包含了一千个三角形的网格。在这两种情况下,GPU的性能消耗其实并没有多大的区别,但CPU的draw call数目就会成为性能瓶颈。因此,批处理的思想很简单,就是在每次调用draw call时尽可能多地处理多个物体。

那么什么样的物体可以一起处理?答案是使用一个材质的物体。因为对于使用一个材质的物体,她们之间的不同仅仅在于顶点数据的差别。我们可以把这些顶点数据合并在一起,在一起发送给GPU,就可以完成一次批处理。

unity中支持两种批处理方式:一种是动态批处理,一种是静态批处理。对于动态批处理来说,优点是一切处理都是unity自动完成的,不需要我们自己做任何操作,而且物体是可以移动的,缺点是限制很多,可能一不小心就破坏了这种机制,导致unity无法动态批处理一些使用了相同材质的物体。而对于静态批处理来说,它的优点是自由度很高,限制很少;但缺点是可能会占用更多的内存,而且经过静态批处理后的所有物体都不可以再移动了(即便在脚本中尝试改变物体的位置也是无效的)。

一、动态批处理

如果场景中有一些模型共享了同一个材质并满足一些条件,unity就可以自动把它们进行批处理,从而只需要花费一个draw call就可以渲染所有的模型。动态批处理的基本原理是,每一帧把可以进行批处理的模型网格进行合并,合并后模型数据传递给GPU,然后使用同一个材质对其渲染。除了实现方便,动态批处理的另一个好处是:经过批处理的物体仍然可以移动,这是由于在处理每帧时Unity都会重新合并一次网格。

虽然unity的动态批处理不需要我们进行任何额外工作,但只有满足条件的模型和材质才可以被动态批处理。需要注意随着unity版本的变化,这些条件也有一些改变,我们下面给出一些主要条件限制。

1. 能够进行动态批处理的网格的顶点属性规模要小于900。例如shader中需要使用顶点位置、法线和纹理坐标这3个顶点属性,那么想要让模型能够被动态批处理,它的顶点数目不能超过300。需要注意的是,这个数字在未来有可能会发生变化,因此不要依赖这个数据。

2. 一般来说,所有对象都需要使用同一个缩放尺度(可以是(1,1,1)、(1,2,3),(1.5,1.4,1.3)等,但必须都一样)。一个例外情况下,如果所有的物体都使用了不同的非同一缩放,那么他们也是可以被动态批处理的。但在unity5中,这种对模型缩放的限制已经不存在了。

3. 使用光照纹理(lightmap)的物体需要小心处理。这些物体需要额外的渲染参数,例如在光照纹理上的索引、偏移量和缩放信息等。因此为了让这些物体可以被动态批处理,我们需要保证他们指向光照纹理中的同一个位置。

4. 多Pass的shader会中断批处理,在前向渲染中,我们有时需要额外的Pass来为模型添加更多的光照效果,但这样一来模型就不会被动态批处理了。

下面看一个场景,里面有3个立方体,他们使用了同一个材质,同时还包含了使用其他材质的一个球体。场景中还包含了一个平行光,关闭了阴影效果,以避免阴影计算对批处理数目的影响,这个场景的渲染统计数据如下图:

unity中的渲染优化技术——(减少draw call数目 方法一:动态批处理)

可以看出要渲染这样一个包含了4个物体的场景共需要3个批处理(untiy5中是2个,我用的是unity2018,我们发现有一个draw call进行了clear操作,具体作用目前还不清楚,先不用管他,我们以unity5为参考),其中一个批处理用于绘制经过动态批处理合并后的3个立方体网格,另一个批处理用于绘制球体。我们可以从Save by batching看出批处理帮我们节省了2个draw call。

现在我们再向场景中添加一个点光源,并调整它的位置使他可以照亮这4个物体,由于场景中的物体都使用多个Pass的shader,因此点光源会对他们产生光照影响,如下图:

unity中的渲染优化技术——(减少draw call数目 方法一:动态批处理)

unity中的渲染优化技术——(减少draw call数目 方法一:动态批处理)

我们发现渲染一帧所需的批处理数目增大到了9(untiy5中是8个,我用的是unity2018,我们发现有一个draw call进行了clear操作,具体作用目前还不清楚,先不用管他,我们以unity5为参考),而Save by batching的数目变成了0。这是因为多个Pass的shader在需要应用多个光照的情况下,破坏了动态批处理的机制,导致unity不能对这些物体进行动态批处理。而由于平行光和点光源需要对4个物体分别产生影响,因此需要2X4个批处理操作。我们从FrameDebug查看,平行光产生影响的Pass是ForwardBase的渲染路径,而点光源产生影响的Pass是ForwardAdd的渲染路径。只有物体在点光源的影响范围内,unity才会调用额外Pass处理它。因此,如果场景中点光源的距离物体很远,仍然会被动态批处理的。

动态批处理的限制条件比较多,例如很多时候我们的模型数据往往会超过900个顶点属性限制,这种时候依赖动态批处理来减少draw call显然已经不能满足我们需求了,这时我们可以使用静态批处理技术,下篇文章介绍。

版权声明:本文为CSDN博主「小橙子0」的原创文章,
遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/cgy56191948/article/details/102916533

最新文章