注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

重新出发的阿赵

阿赵的博客

 
 
 

日志

 
 

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

2017-10-21 09:44:22|  分类: Unity教程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
最近我一直在解决项目里面打包AssetBundle的容量问题,还是比较有成果。包体容量从原来的800M减少到400M左右。
其中一个可以减少容量的地方,是Batching Static。这其实是有历史原因的,由于上一个项目的失败,这个项目被老板指定使用其他某个项目组的框架代码来开发。由于这个框架是使用LoadLevel的方法来加载整个场景的,所以最后会把整个Scene文件打包成AssetBundle。原来的框架是整个Scene不打依赖整个打成AssetBundle,所以单个AssetBundle包体很大,而且有冗余,所以我改成了把Scene用到的fbx、材质贴图都分别打成依赖。
说这个优化之前,先来说说Batching Static是干什么用的。我现在拿了一个场景来做实验。我们固定一个摄像机的角度。在正常情况下,这个场景的Batches是161,而SetPass calls是42:
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 
正常的情况下,我们会把场景里面所有模型的Batching Static选型勾选上。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 
这次再运行场景,会发现发生了变化,Batches变为30,SetPass calls变成26。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 
看起来这个选项的作用很大,把很多需要动态合并的网格变成了静态合并。不过这个处理并不是完全没有代价的,我们在profile里面看看内存,可以看出,实际上Unity是把我们用到的网格模型合并成一个或者多个mesh,并保存在内存里面。为什么是生成了多个呢?这是因为,unity单个mesh的顶点数上限是65000个,如果合并的时候,单个mesh快要超过65000的时候,就会再分一个新的网格出来。这些网格的定点数据,现在是占了33.4M内存。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 
然后场景里面所有用到mesh的地方,将会自动替换成自动生成的Combied Mesh
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
这是某些模型的合并情况,这个合并后的模型已经62000多定点了。
 
接下来说说这个Batching对打包的Scene文件的AssetBundle容量的影响。刚才说过,我已经把依赖打包的方式修改过,Scene用到的所有FBX和材质贴图都已经单独打包成依赖。所以当前打包出去的Scene文件的AssetBundle应该是没有任何额外的资源信息的。在勾选了Batching Static的情况下,打出来的单个Scene文件的AssetBundle有9M多。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 这个AssetBundle里面,按道理说,是包含了Scene本身的数据,比如模型的关联信息和Transform信息,光照信息等。这个场景的LightMap并不大,打包出去不可能有9M多的容量。剩下的容量,其实就是Batching合并的网格的顶点数据。这样可以理解成,如果当前场景的模型总顶点数越大,在勾选了Batching后,打包出来的AssetBundle的容量就会越大。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵

下面我把Batching的选项去掉,再次打包AssetBundle,这次单个Scene的AssetBundle容量就变为了1M左右了。
 解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
那么,问题就来了。这似乎是一个矛盾。如果想用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,结果如下:
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
可以看到,这个结果是和在Unity里面勾选了Batching Static是一样的。

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

必须注意一点,如果要使用这个API来合并Batching,对于使用的FBX必须把可读写的选项勾上,不然是合并不了的。这点非常的重要。
解决Batching Static静态合并网格的容量问题 - 阿赵 - 重新出发的阿赵
 

最后贴一下我按照相同材质来合并的代码:
 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);
            
        
    }
  评论这张
 
阅读(66)| 评论(2)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017