第一家,天神互动的题把,有这个,当时直接朦le~~(一个一年游戏开发小菜鸟)

一、什么是批处理?

 

我们知道Unity3D在屏幕上绘制一个图形本质上调用OpneGL或者DirectX这样的API,因此在这个过程中会产生一定程度上的性能消耗。DrawCall是OpenGL中描述绘制次数的一个量,例如一个基本的OpenGL绘制流程是设置颜色->绘图方式->顶点坐标->绘制->结束,在绘制的过程中每帧都会重复这个过程,这就是一次DrawCall,所以当游戏中的绘制过程变得复杂的时候,就会带来DrawCall的急剧增加,进而带来游戏的性能问题,反映到游戏表现上就变成了优化问题。

 

那么在Unity3D中采取了什么样的措施来降低DrawCall呢?这就是我们今天要说的批处理,换句话说Unity3D使用了批处理来达到降低DrawCall的目的,批处理希望通过对物体网格的重组来获得更高的绘制效率,试想以下如果将多个物体合并为一个物体,那么在绘制的时候只需要绘制一次就够了,因此从这个角度上来讲这样做肯定是可以降低DrawCall的,更深刻的一种理解是这里体现了一种资源循环调用的思想,接触过android开发的朋友们一定知道ListView控件可以对其元素进行“缓存”从而提高效率,因为我们可以发现其实ListView是对列表项进行某种程度上的“复用”从而提高了效率,在Unity3D这里同样遵循了这个原理。

 

在Unity3D中进行批处理的一个前提是相同材质的物体可以被合并,如果这些物体使用不同的材质,那么当我们把这些材质对应的纹理打成“图集”以后可以对其进行合并,并且在合并的时候应该是用Renderer.sharedMaterial 而非 Renderer.material以保证材质是可以共享的。关于DrawCall的相关细节大家从这里来了解,博主并未对图形学领域有过深入的研究,因此就不在这里班门弄斧了啊,哈哈!

 

二、Unity3D中批处理的两种方式

 

在Unity3D中有静态批处理和动态批处理两种方式,下面我们就来分别说说这两种不同的批处理方式!

 

静态批处理

 

静态批处理其实大家都是知道的。为什么这样说呢?因为我们在使用Unity3D的过程中无形中培养了这样一个习惯,那就是将场景中相对来说“静态”的物体都勾选Static选项,这在Unity3D中称为Static GameObjects,并且因为这一特性和Lightmapping、Navigation、Off-meshLinks、ReflectionProbe、Occluder and Occludee等内容均有着密切的联系,因此说静态批处理大家都是知道的其实一点都为过,和场景优化相关的内容博主会在后续的博客中涉及。

 

静态批处理允许游戏引擎尽可能多的去降低绘制任意大小的物体所产生的DrawCall,它会占用更多的内存资源和更少的CPU资源,因为它需要额外的内存资源来存储合并后的几何结构,如果在静态批处理之前,如果有几个对象共享相同的几何结构,那么将为每个对象创建一个几何图形,无论是在编辑器还是在运行时。这看起来是个艰难的选择,你需要在内存性能和渲染性能间做出最为正确的选择。

 

在内部,静态批处理是通过将静态对象转换为世界空间,并为它们构建一个大的顶点+索引缓冲区。然后,在同一批中,一系列的“便宜”画调用,一系列的“便宜”,几乎没有任何状态变化之间的。所以在技术上它并不保存“三维的调用”,但它可以节省它们之间的状态变化(这是昂贵的部分)。使用静态批处理非常简单啦,只要勾选物体的Static选项即可!

 

动态批处理

 

相对静态批处理而言,动态批处理的要求更为严格一些,它要求批处理的动态对象具有一定的顶点,所以动态批处理只适用于包含小于900个顶点属性的网格。

 

如果你的着色器使用顶点位置,法线和单光,然后你可以批处理300个顶点的动态对象;而如果你的着色器使用顶点位置,法线,uv0,UV1和切线,那么只能处理180个顶点的动态对象。接下来最为重要的一点,如果动态对象使用的是不同的材质,那么即使进行了动态批处理从效率上来讲并不会有太大的提升。如果动态对象采用的是多维子材质,那么批处理是无效的。

 

如果动态对象接收实时光影,同样批处理是无效的。点击原文链接查看代码~

 

这段脚本的核心是CombineMeshes()方法,该方法有三个参数,第一个参数是合并实例的数组,第二个参数是是否对子物体的网格进行合并,第三个参数是是否共享材质,如果希望物体共享材质则第三个参数为true,否则为false。

 

在我测试的过程中发现,如果选择了对子物体的网格进行合并,那么每个子物体都不能再使用单独的材质,默认会以第一个材质作为合并后物体的材质

那么批处理对游戏效率提升究竟有怎样的作用呢?我们来看下面几组测试对比:

 

1、三个不同的物体使用同一种材质,不做静态批处理,不做动态批处理:DrawCall为4、面数为584、顶点数为641

 

2、三个不同的物体使用同一种材质,只做静态批处理,不做动态批处理:DrawCall为2、面数为584、顶点数为641

 

3、三个不同的物体使用不同的材质,不做静态批处理,不做动态批处理:DrawCall为4、面数为584、顶点数为641

 

4、三个不同的物体使用不同的材质,只做静态批处理,不做动态批处理:DrawCall为4、面数为584、顶点数为641

 

5、三个不同的物体使用不同的材质,不做静态批处理,只做动态批处理:DrawCall为4、面数为584、顶点数为641

 

6、三个不同的物体使用不同的材质,做静态批处理,做动态批处理:DrawCall为4、面数为584、顶点数为641

 

7、三个不同的物体使用同一种材质,不做静态批处理,只做动态批处理:DrawCall为4、面数为584、顶点数为641         

 

 

1-如果不同的物体间共享材质,则可以直接通过静态批处理降低DrawCall 

 

2、动态批处理并不能降低DrawCall、面数和顶点数(我不会告诉你我昨天傻呵呵地合并了好多场景中的模型,结果面数和顶点数并没有降下来,23333) 

 

3、不管是静态批处理还是动态批处理都会影响Culiing,这同样是涉及到场景优化的一个概念;