解决Batching Static静态合并网格的容量问题

最近我一直在解决项目里面打包AssetBundle的容量问题,还是比较有成果。包体容量从原来的800M减少到400M左右。
其中一个可以减少容量的地方,是Batching Static。这其实是有历史原因的,由于上一个项目的失败,这个项目被老板指定使用其他某个项目组的框架代码来开发。由于这个框架是使用LoadLevel的方法来加载整个场景的,所以最后会把整个Scene文件打包成AssetBundle。原来的框架是整个Scene不打依赖整个打成AssetBundle,所以单个AssetBundle包体很大,而且有冗余,所以我改成了把Scene用到的fbx、材质贴图都分别打成依赖。
说这个优化之前,先来说说Batching Static是干什么用的。我现在拿了一个场景来做实验。我们固定一个摄像机的角度。在正常情况下,这个场景的Batches是161,而SetPass calls是42:
这里写图片描述

正常的情况下,我们会把场景里面所有模型的Batching Static选型勾选上。
这里写图片描述

这次再运行场景,会发现发生了变化,Batches变为30,SetPass calls变成26。
这里写图片描述

看起来这个选项的作用很大,把很多需要动态合并的网格变成了静态合并。不过这个处理并不是完全没有代价的,我们在profile里面看看内存,可以看出,实际上Unity是把我们用到的网格模型合并成一个或者多个mesh,并保存在内存里面。为什么是生成了多个呢?这是因为,unity单个mesh的顶点数上限是65000个,如果合并的时候,单个mesh快要超过65000的时候,就会再分一个新的网格出来。这些网格的定点数据,现在是占了33.4M内存。
这里写图片描述

然后场景里面所有用到mesh的地方,将会自动替换成自动生成的Combied Mesh
这里写图片描述

这里写图片描述
这是某些模型的合并情况,这个合并后的模型已经62000多定点了。

接下来说说这个Batching对打包的Scene文件的AssetBundle容量的影响。刚才说过,我已经把依赖打包的方式修改过,Scene用到的所有FBX和材质贴图都已经单独打包成依赖。所以当前打包出去的Scene文件的AssetBundle应该是没有任何额外的资源信息的。在勾选了Batching Static的情况下,打出来的单个Scene文件的AssetBundle有9M多。
这里写图片描述
这个AssetBundle里面,按道理说,是包含了Scene本身的数据,比如模型的关联信息和Transform信息,光照信息等。这个场景的LightMap并不大,打包出去不可能有9M多的容量。剩下的容量,其实就是Batching合并的网格的顶点数据。这样可以理解成,如果当前场景的模型总顶点数越大,在勾选了Batching后,打包出来的AssetBundle的容量就会越大。
这里写图片描述

下面我把Batching的选项去掉,再次打包AssetBundle,这次单个Scene的AssetBundle容量就变为了1M左右了。
这里写图片描述
那么,问题就来了。这似乎是一个矛盾。如果想用Batching Static静态合并,似乎就必须增大AssetBundle的容量。或者换个说法,如果想AssetBundle的容量小,似乎就没有办法使用Batching Static静态合并?
事实上Unity是提供了API可以在运行的时候设置Batching的,具体的API为:
public static void Combine(GameObject staticBatchRoot);
public static void Combine(GameObject[] gos, GameObject staticBatchRoot);
这就是说,你可以选择把所有物体放在一个父节点下面,然后调用第一个API,这样Unity就会自动帮你设置静态合并,也可以使用第二个API自己组装GameObject的数组,来控制哪些GameObject是组合在一起的。

先来看看第一个API,我把所有的模型放在了一个父节点下,然后执行第一个API,结果如下:
这里写图片描述
可以看到,这个结果是和在Unity里面勾选了Batching Static是一样的。

然后,我一直在思考一个问题,我观察过之前Unity自动Batching合并出来的Mesh,发现它把很多使用不同的Material的模型Mesh都合并成了同一个。按照我自己的理解,在合并Mesh的时候,应该是把使用相同材质的Mesh合并在一起,这样渲染才是最优的。所以我用第二个API,控制了一下合并网格的规则,把同样材质的Mesh才合并在一起。
结果如下:
这里写图片描述
可以看出,SetPass calls和之前一样,但Batches比Unity自动合并的要低一点。
这里写图片描述
从内存上看,这时候合并的Mesh内存会稍微低一点。但这个内存容量我觉得并不能作为参考。因为最终合并成多少个网格存在一定的运气成分,如果满了65000顶点数,将会再开一个新的Mesh。在截图里面看到的在Mesh名字后面有2 、3 这样的数字的,其实就是因为超过顶点数而必须另外新增的Mesh。

必须注意一点,如果要使用这个API来合并Batching,对于使用的FBX必须把可读写的选项勾上,不然是合并不了的。这点非常的重要。
这里写图片描述

最后贴一下我按照相同材质来合并的代码:

 private void StaticMeshFun()
{
    MeshRenderer[] mrs = GameObject.FindObjectsOfType<MeshRenderer>();
    combineList = new Dictionary<Material, List<GameObject>>();
    for (int i = 0; i < mrs.Length; i++)
    {
        Add2CombineList(mrs[i]);
    }

    int index = 0;
    foreach (KeyValuePair<Material, List<GameObject>> item in combineList)
    {
        index++;
        CreateStaticMesh(item.Value, index);
    }
}

private GameObject meshRoot;
private void CreateStaticMesh(List<GameObject> objs,int index)
{
    if(meshRoot==null)
    {
        meshRoot = new GameObject("CombineMesh");
    }
    GameObject[] gos = new GameObject[objs.Count];
    for(int i = 0;i<objs.Count;i++)
    {
        gos[i] = objs[i];
    }
    StaticBatchingUtility.Combine(gos, meshRoot);
}

private void Add2CombineList(MeshRenderer mr)
{
    if(combineList.ContainsKey(mr.sharedMaterial)==false)
    {
        combineList.Add(mr.sharedMaterial, new List<GameObject>());
    }
    combineList[mr.sharedMaterial].Add(mr.gameObject);
        
    
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值