动态批处理不生效怎么办?我相信很多没有经验的人对此无能为力。为此,本文总结了问题产生的原因及解决方法。希望你能通过这篇文章解决这个问题。
最近在项目开发过程中,偶然发现游戏场景的绘制占用了很多batch,几乎一个模型显示占用了一个Batch,批处理保存的数量几乎为零,也就是没有进行批处理渲染优化。显然,这与预期相差甚远,因为场景中虽然有上百个模型可以绘制,但大部分都是完全相同的卡片模型,引用相同的材质球,大部分都可以由Unity自动动态批量进行合并和批量处理。到底出了什么问题?
所以看一下Unity手册,看看动态配料的规则,可以总结如下:
通常,Unity只支持网格小于900个顶点的对象的动态批处理。如果在着色器中使用顶点位置、法线和紫外线值,则仅支持300个或更少顶点的对象。如果使用顶点位置、法线、UV0、UV1和切线向量,则仅支持180个顶点以下的对象。
如果两个对象的比例只是镜像,比如分别为(-1,-1,-1)或(1,1,1),则不会动态批处理。
具有不同参考材质实例的对象不会被动态批处理,即使两个对象的材质没有本质上的不同。一个简单的例子意味着同一素材被实例化为两个副本,分别被A和B引用,所以A和B不会被动态批处理,因为它们引用了两个不同的例子。
具有光照贴图的对象不会被动态批处理,除非它们指向光照贴图的相同部分。
具有多个通道的着色器对象不会被动态批处理。
经过简单考察,该模型在1、2、4、5点满足Unity的要求。所以我开始怀疑不同的材质实例引用导致了问题。
查看检查器,我们可以看到网格渲染器引用的材质确实是需要的着色器,但是后面添加了单词Instance。单击图中的红色框,“项目”窗口不会跳转到着色器文件的所有位置,这表明该材质是在运行时实例化的,而不是Prafab在预设期间指向的着色器引用。由此,我们可以推断每个模型实例化自己的材质实例,这导致没有动态批处理。如果你知道问题的原因,你需要知道问题是什么。检查绑定在对象上的脚本,并检查与材料相关的代码。你可以看到有两个地方:
privateTexture _ mainPlaneTexprivateTexture _ flashPlaneTexpublicmesrendererm _ rendererPlane;private material _ cached plane material=null;privatatematerialcachedplanemat
{获取
{ if(_ cached plane material==nullm _ render plane!=null)
{
_ cachedPlaneMaterial=m _ rendererplane . material;
} return _cachedPlaneMaterial;
}
}
private void SetGray(bool bGray) { if (bGray) { cachedPlaneMat.SetTexture("_MainTex", _flashPlaneTex); } else { cachedPlaneMat.SetTexture("_MainTex", _mainPlaneTex); } }
这个模型有模型变灰色的需求,实现方式是通过获取该物体的material,再视情况将MainTexture设置为正常/变灰贴图。出问题的地方十有八九就在这块代码。查看MeshRenderer的接口,原来Unity提供了两个获取Material的方法接口,分别是material及sharedMaterial。
搞懂这两个接口的区别,弄清楚Unity在底层到底搞了什么鬼,估计离真相就不远了。
Renderer.material
Returns the first instantiated Material assigned to the renderer.
Modifying material will change the material for this object only.
If the material is used by any other renderers, this will clone the shared material and start using it from now on.
Unity官方说当使用Renderer.material获取Material引用时,会把Render里Materials列表第一个预设的Material进行实例化,并将返回实例。这样,当我们对这个物体的Material进行修改时,只会修改实例,而不会修改到最原本的材质球,比如我们将同个模型Prafab拖放N多个到场景里,当我们对其中一个模型A的材质球进行修改,比如替换贴图,其他模型是不会受到影响的,因为A修改的仅仅只是自己实例化出来的材质球。
Renderer.sharedMaterial
The shared material of this object.
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
而如果调用Renderer.sharedMaterial接口,Unity则不会多此一举地帮我们做实例化,再返回实例了,直接就是返回最原本的材质球。还是上面那个例子,当我们给A模型的材质球替换贴图,会发现场景的所有模型都被替换了贴图。
看完了两个接口的差异,也终于明白了问题的根本原因。为了实现模型变灰的需求,代码里使用Renderer.material来获取material实例,殊不知每个物体都各自持有了一份不同的material实例,使得Unity没办法将这些一模一样的物体进行合批渲染,当同屏显示的物体数目多达上百,就意味着多出上百个不必要的Drawcall,多了不必要的损耗。
而这个问题也不能简单地将Renderer.material改为调用Renderer.sharedMaterial就解决了,总不能把一个模型变灰,所有模型都要跟着变灰吧?正确的解决方法就是预设正常/灰色两个材质球,根据不同的情况替换材质。
最后贴下优化前后,场景Drawcall的对比:
优化前
优化后
可以看到,在同屏模型较多的情况下,Batch数由近100骤降到2,基本都被Unity动态合批处理了。
看完上述内容,你们掌握Dynamic Batching不生效该怎么办的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/94851.html