我们知道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中有静态批处理和动态批处理以及GPU Instancing。下面我们就来分别说说这三种不同的批处理方式!
静态批处理
因为我们在使用Unity3D的过程将场景中相对来说“静态”的物体都勾选Static选项,这在Unity3D中称为Static GameObjects,并且因为这一特性和Lightmapping、Navigation、Off-meshLinks、ReflectionProbe、Occluder and Occludee等内容均有着密切的联系。静态批处理允许游戏引擎尽可能多的去降低绘制任意大小的物体所产生的DrawCall,它会占用更多的内存资源和更少的CPU资源,因为它需要额外的内存资源来存储合并后的几何结构,如果在静态批处理之前,如果有几个对象共享相同的几何结构,那么将为每个对象创建一个几何图形。使用静态批处理非常简单啦,只要勾选物体的Static选项即可!
静态批处理会在构建时将多个静态网格物件合并为一个或多个大的网格物件,然后在运行时一次批处理渲染一个大网格中的多个物件。如果不同的物体间共享材质,则可以直接通过静态批处理降低DrawCall
动态批处理
相对静态批处理而言,动态批处理的要求更为严格一些,它要求批处理的动态对象具有一定的顶点,所以动态批处理只适用于包含小于900个顶点属性的网格。如果你的着色器使用顶点位置,法线和单光,然后你可以批处理300个顶点的动态对象;而如果你的着色器使用顶点位置,法线,uv0,UV1和切线,那么只能处理180个顶点的动态对象。接下来最为重要的一点,如果动态对象使用的是不同的材质,那么即使进行了动态批处理从效率上来讲并不会有太大的提升。如果动态对象采用的是多维子材质,那么批处理是无效的。如果动态对象接收实时光影,同样批处理是无效的。动态批处理并不能降低DrawCall、面数和顶点数
动态批处理在每帧中获取多个小型网格物件,在CPU中对其进行顶点变换,将相似的顶点组合到一起,然后一次绘制它们。
GPU Instancing
可以利用少量Draw Call绘制多个具有不同的位置、旋转以及其他着色器属性的相同对象。
有时在编辑器中可以清楚地看到,一些本应被批处理的对象出于某些原因没有被批处理。首先,请检查Player Settings中是否启用批处理功能。这个步骤看似多余,但我们遇到太多的无法处理的原因都是因为忘记开启。
我们专门为此提供了展示项目来演示Unity在什么情况下必须发起新的批处理请求。首先下载项目并复制到Unity项目中。请注意,你需要安装Unity 5.6才能看到Frame Debugger中关于批处理状态的说明。
以下是展示项目(Unity 5.6)中导致无法进行批处理的原因。每个原因对应一次单独的批处理:
AdditionalVertex Streams — 对象使用MeshRenderer.additionalVertexStreams设定了额外的顶点信息流。
DeferredObjects on Different Lighting Layers — 该物件位于另一不同的光照层中。
DeferredObjects Split by Shadow Distance — 两个物体中有一个在阴影距离范围内而另一个不是。
DifferentCombined Meshes — 该对象属于另一个已合并的静态网格。
DifferentCustom Properties — 该对象设定了不同的MaterialProperyBlock。
DifferentLights — 该物件受不同的前向光照(Forward Light)影响。
DifferentMaterials — 该对象使用不同的材质。
DifferentReflection Probes — 该对象受不同的反射探头(Reflection Probe)影响。
DifferentShadow Caster Hash — 该对象使用其他的阴影投射着色器,或是设定了不同的着色器参数/关键词,而这些参数/关键词会影响阴影投射Pass的输出。
DifferentShadow Receiving Settings — 该对象设定了不同的“Receive Shadows”参数,或是一些对象在阴影距离内,而另一些在距离之外。
DifferentStatic Batching Flags — 该对象使用不同的静态批处理设定。
DynamicBatching Disabled to Avoid Z-Fighting — Player Settings中关闭了动态批处理,或在当前环境中为避免深度冲突而被临时关闭。
InstancingDifferent Geometries — 使用GPU Instancing渲染不同的网格或子网格。
LightmappedObjects — 对象使用了不同的光照贴图,或在相同的光照贴图中有不同的光照贴图UV转换关系。
LightprobeAffected Objects — 对象受其他光照探头(Light Probe)影响。
MixedSided Mode Shadow Casters — 对象的“Cast Shadows”设定不同。
Multipass — 对象使用了带多个Pass的着色器。
MultipleForward Lights — 该物件受多个前向光渲染影响。
Non-instanceableProperty Set — 为instanced着色器设定来non-instanced属性。
OddNegative Scaling — 该对象的缩放为很奇怪的负值,例如(1,-1,1)。
ShaderDisables Batching — 着色器使用“DisableBatching”标签显式关闭了批处理。
TooMany Indices in Dynamic Batch — 动态批处理索引过多(超过32k)。
TooMany Indices in Static Batch — 静态批处理中的组合网格索引过多。对于OpenGL ES来说是48k,OSX是32k,其他平台是64k。
TooMany Vertex Attributes for Dynamic Batching — 欲进行动态批处理的子网格拥有超过900个顶点属性。
TooMany Vertices for Dynamic Batching — 欲进行动态批处理的子网格顶点数量超过300个。
如何创建一个新场景地图
https://www.colabug.com/743997.html