优化相关知识02动、静态批处理

抄书笔记:UnityShader入门精要 冯乐乐
最常看到的优化技术就是批处理了。
批处理的实现原理就是为了减少每一帧需要的drawcall数目。为了把每一个对象渲染到屏幕上,CPU需要检查哪些光源影响了该物体,绑定sahder并设置他的参数,再把渲染命令传递给GPU。当场景中包含了大量的对象的时候,这些操作就会非常耗时。
一个极端的例子:如果需要渲染一千个三角形网格,把他们按一千个单独的网格进行渲染所消耗的世界要远远大于渲染一个包含了一千个三角形网格。这两种情况下,GPU的性能消耗其实没有多大的区别,但是CPU的DrawCall数目就会成为性能瓶颈。因此批处理的思想很简单:在每次调用drawCall的时候尽可能处理多个物体。

批处理——使用同一个材质的物体才能一起处理。这是因为使用相同的一个材质的物体,他们之间的不同仅仅在于顶点数据的差别,可以把这些顶点数据合并在一起,在一起发送给GPU,就可以完成一次批处理。
Unity支持两种批处理方式:一种是动态批处理,一种是静态批处理。对于动态批处理来说,优点是一切处理都是Unity自动完成的,不需要我们做任何操作,而且物体是可以移动的。缺点是:限制很多。可能一不小心就会破坏这种机制,导致Unity无法动态批处理一些使用了相同的材质的物体。对于静态批处理来说,他的优点是自由度很高,限制很少;但缺点是可能会占用更多的内存,而成经过静态批处理的所有物体都不可以在移动了。

动态批处理:
如果场景中有一些模型共享了一个材质并满足了一些条件,Unity就可以自动把他们进行批处理,从而只需要花费一个drawcall就可以渲染所有的模型;动态批处理的基本原理是,每一帧把可以进行批处理的模型网格进行合并,再把合并后模型数据传递给GPU,然后使用同一个材质对其渲染。除了实现方便,动态批处理的另一个好处是,经过批处理的物体仍然可以移动,这是由于在处理每帧时Unity都会重新合并一次网格。
虽然Unity的动态批处理不需要我们进行任何额外的工作,但是只有满足条件的模型和材质才可以被动态批处理。
限制动态批处理的主要条件:
·能够进行动态批处理的网格顶点属性规模要小于900。例如:如果shader中需要使用顶点位置、法线和纹理坐标这 3个顶点属性,要么要想让模型能够被动态批出,他的顶点数目不能超过300.需要注意的是,这个数字在未来可能会有可能发生变化,因此不要依赖这个数据。
·一般来说,所有对象都需要使用同一个缩放尺寸。有一种例外情况,如果所有的物体都使用了非同一缩放,那么他们也是可以被动态批处理的。(Unity5已经取消了这种限制)
·使用光照纹理的物体需要小心处理。这些物体需要额外的渲染参数,例如在光照纹理的索引、偏移量和缩放信息等。因此为了让这些物体可以被动态批处理,需要保证他们指向光照纹理的同一个位置;
·多Pass的shader会中断批处理,在前向渲染中,有时需要使用额外的Pass来为模型添加更多的光照效果,但这样一来模型就不会被动态批处理了。
使用了多个Pass的Shader在需要应用多个光照的情况下,破坏了动态批处理的机制,导致Unity不能对这些物体进行动态批处理,由于平行光和点光源需要对4个物体分别产生影响,因此,需要2*4个批处理操作。需要注意的是,只有物体在点光源的影响范围内,Unity才会调用额外的Pass来处理他。因此如果场景中的物体距离点光源比较远的情况下,也是可以被动态批处理的。
动态批处理的条件限制比较多,很多时候,我们的模型数往往会超过900的顶点属性限制,这种哦时候依赖动态批处理来减少drawcall仙人已经不能够满足我们的需求了。这个时候就能使用静态批处理了。

静态批处理:
静态批处理使用任何大小的几何模型。他的实现原理是:只要运行开始阶段,把需要进行静态批处理的模型合并到一个新的网格结构中,这意味着这些模型不可以在运行时刻被移动。但由于他只需要进行一次合并操作,因此,比动态批处理更加高效;静态批处理的另一个缺点在于,它往往需要占用更多的内存来存储合并后的几何结构。这是因为,如果在静态批处理前一些物体共享了相同的网格,那么在内存中每一个物体都会对应该网格的复制品,即一个网格会变成多个网格再放送给GPU。
比如:一个使用了1000个网格相同树模型的森林中使用了静态批处理,那么就会多食用1000倍的内存,这会造成严重的内存影响。这种时候,要么忍受这种牺牲内存换取性能的方法,要么不要使用静态批处理,而使用动态批处理技术(但是要控制顶点数),或者自己编写批处理的方法。
静态批处理的方法实现非常简单,只需要吧物体面板上static复选框勾选上即可。(实际上只需要勾选batching static)
如果在运行时查看每个模型使用的网格,就会发现他们都会变成一个名为Combined Mesh的东西,这个网格是Unity合并了所有被标识为Static的物体的结果。这个合并后的网格,其实包含了各个物体的子网格,对于合并后的网格,Uniy会判断其中使用了同一个材质的子网格,然后对他们进行批处理。
在内部实现上:Unity首先把这些静态物体变换到世界空间下,然后为他们构建一个更大的顶点和索引缓存; 对于使用了统一材质的物体,Unity只需要调用一个DrawCall就可以绘制全部的物体。而对于使用了不同材质的物体,静态批处理同样可以提升渲染性能,机关这些物体仍然需要调用多个drawcall,但是静态批处理可以减少这些draw call之间的状态切换。切换状态往往是耗时的操作;
在Unity的分析器中观察到在应用静态批处理前后VBO Total的变化。VBO(Vertex Buffer Object)顶点缓冲对象的数目变大了;这是因为静态批处理会占用更多的内存。静态批处理需要占用更多的内存来存储合并后的几何结构体,如果一些物体共享了相同的网格,那么内存中每一个物体都会对应个该网格的复制品。

共享材质:
无论是动态批处理还是静态批处理,都要求模型之间需要共享同一个材质。但是不同的模型之间总会需要不同的渲染属性,例如使用不同的纹理、颜色等。所以需要一些策略尽可能的合并材质;
如果两个材质之间只有使用的纹理不同,我们可以把这些纹理合并到一张更大的纹理中,这张更大的纹理统一被称为图集atlas。一旦使用了同一张纹理,我就可以使用同一个材质,在使用不同的采样坐标对纹理采样即可;
但是有时候,除了纹理不同以外,不同的物体在材质上还有一些微小的参数变化,例如,颜色不同,某些浮点属性不同。但是不管是动态批处理还是静态批处理,他们的前提是都要使用同一个材质,是同一个,而不是使用了同一种shader材质。也就是说他们指向的材质必须是同一个实体。这意味着,只要调整了参数,就会影响到所有使用这个材质的对象,那么想要微小的调整,常用的方法就是使用网格的顶点数据(最常见的就是顶点颜色的数据)来存储这些参数;

经过批处理后的物体会被处理成更大的VBO发送给GPU,VBO的数据可以作为输入传递给顶点着色器,因此,可以巧妙的对VBO中的数据进行控制,从而达到不同的效果的目的。一个例子是,森林场景中所有的树木都是用了同一种材质,希望他们可以通过批处理来减少drawcall,但不同的树木的颜色可能不同,这时候就可以利用网格的顶点的颜色数据来调整;

需要注意的是,如果需要在脚本中访问共享材质。应该使用Renderer.shareMaterial来保证修改的是和其他物体共享的材质,这是这意味着修改会应用到所有该材质的物体上。另一个类似的API是Renderer.Material是来修改材质,Unity会创建一个该材质的复制品,从而破坏批处理在改物体上的应用,
批处理的使用注意事项:
在选择使用动态批处理还是静态批处理的时候一些建议:
1、尽可能渲染静态批处理,但是得时刻注意对内存的消耗,并且记住经过静态批处理的物体不可以在移动;
2、如果无法进行静态批处理,而要选择动态批处理的话。那么请小心不要打破动态批处理的限制。例如:尽可能让这样的物体变少,并且尽可能让这些物体包含少量的顶点属性和顶点数目。
3、对于游戏中的小道具,例如可以捡起的金币等,可以使用动态批处理。
4、对于包含动画的这类物体,无法全部使用静态批处理,但是其中如果有不动的部分,可以把这部分表示成Static。
除了以上的提示之外,在使用批处理的时候还有一些需要注意的地方,由于批处理需要把多个模型变换到世界空间下在合并他们,如果在shader中存在一些基于模型空间下的坐标计算,那么往往可能得到一个错误的效果。解决办法就是:在sahder中使用DisableBatching标签来强制使用该shader的材质不会被批处理;另一个注意事项:使用半透明材质的物体通常需要使用严格的从后往前的绘制顺序来保证透明的混合的正确性。对于这些物体,Unity会首先会保证他们的绘制顺序,在尝试对他们进行批处理。这意味着当绘制顺序无法满足的时候,批处理无法在这些物体上被应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值