性能优化-场景分块

版本记录

日期版本说明作者
2020-1-2 0.1 文档初始版本 李俊

目的

  • 加速场景加载,减少loading时间
  • 提升场景渲染效率

概念

  • 动态合批
    • 接口
      public void CombineMeshes(CombineInstance[] combine, bool mergeSubMeshes, bool useMatrices);
    • 核心代码
foreach (var subItem in itemValue)
              {
                  CombineInstance combine = new CombineInstance();
                  ReanderData matReanderData = subItem.Value;
                  combine.mesh = matReanderData._MeshFilter.sharedMesh;
                  combine.transform = matReanderData._MeshFilter.transform.localToWorldMatrix;
                  combines[index] = combine;
                  index++;
                  if (combineData == null)
                  {
                      combineData = matReanderData;
                  }
                  delList.Add(matReanderData._MeshFilter.gameObject);
                  if(maxValue < matReanderData.scaleInLightmap)
                  {
                      maxValue = matReanderData.scaleInLightmap;
                  }
              }
              
              Mesh newMesh = new Mesh();
              newMesh.CombineMeshes(combines, true, true);
              newMesh.name = combineData._Material.name;
private void CombineObj( GameObject obj )
    {
        Log.Sys("Combine Scene", obj.name);
        var anis = obj.GetComponentsInChildren<Animator>();
        if (anis.Length > 0)
        {
            var meshRenders = obj.GetComponentsInChildren<MeshRenderer>();
            var gos = new List<GameObject>();
            var waterlayer = LayerMask.NameToLayer("Water");
            for (int i = 0; i < meshRenders.Length; i++)
            {
                var go = meshRenders[i].gameObject;
                if (go.layer != waterlayer)
                {
                    var isHaveAniP = go.transform.parent.GetComponent<Animator>();
                    var isHaveAni = go.transform.GetComponent<Animator>();
                    if (isHaveAniP == null && isHaveAni == null)
                        gos.Add(go);
                }
            }
            StaticBatchingUtility.Combine(gos.ToArray(), obj);
        }
        else
            StaticBatchingUtility.Combine(obj);
    }

原理

  • 把场景节点分成必需部分和分块
  • 必需部分加载完成,即认为场景加载完成
  • 必需部分保存成1个prefab,每个块保存成prefab
  • 加载场景时,低优先级加载块prefab,只加载不卸载

制作细节

  • _min 表示该节点下不再拆分,即所有子节点只会在同一个块prefab下
  • 默认都是可以分块的,必需部分需要在always节点下

合批方案


    • 完全依赖运行时instance合批,

      • 缺点:
        • 默认instance合批,不支持lightmap, 需要额外开发instance合批,并且需要大量修改配套的shader
        • 相同材质,不同模型的,无法合批
      • 优点:
        • 不需要生成额外的模型资源,加载友好
        • 跨块的也可以提升性能
    • 动态合批
      bake时,把所有材质球一样的模型合并

      • 缺点:
        • 需要重新烘焙,耗时比较长
        • 生成新的模型资源,加载不友好
        • 烘焙效果可能和原来有差异,因为烘焙参数
        • 跨块无法合批
      • 优点
        • 效率最优
        • 可以处理不同模型,相同材质
        • 兼容性好
        • 开发量相对instance方式小很多
    • 静态合批
      bake时,把每个块内静态合批,必需部分静态合批

      • 缺点:
        • 需要生成大的mesh,对加载最不友好
        • 无法处理跨块的合批
        • 多场景模式下,会有额外的问题
      • 优点:
        • 不需要重新烘焙
        • 可以处理不同模型,相同材质
        • 开发量相对instance小很多

posted on 2020-01-07 14:40  marcher  阅读(400)  评论(0编辑  收藏  举报

导航