• Unity中的网格与材质球合并


    https://blog.csdn.net/dardgen2015/article/details/51517860

    很多时候我们需要把具有相同shader的材质球合并,从而减少drawcall的产生。  

    比如九龙战里面,一个人物带有10个部位,10个部位各自来自不同的fbx文件,加上身体,就有11个材质球,占上11个drawcall。如果主城里面跑着10个角色,光人物就占了110个drawcall!所以这种时候材质球合并是必须的。

    (下图为九龙战里面的多部位换装效果)

    材质球合并,分以下几步走,首先我们讨论普通的MeshRenderer的材质球合并,然后再讨论SkinnedMeshRenderer的材质球合并。

    普通的MeshRenderer的材质球合并:

    1.合并所有材质球所携带的贴图,新建一个材质球,并把合并好的贴图赋予新的材质球。

    2.记录下每个被合并的贴图所处于新贴图的Rect,用一个Rect[]数组存下来。

    3.合并网格,并把需要合并的各个网格的uv,根据第2步得到的Rect[]刷一遍。

    4.把新的材质球赋予合并好的网格,此时就只占有1个drawcall了。

    下面是关键代码:

    1.  
      void CombineMesh()
    2.  
      {
    3.  
       
    4.  
      MeshFilter[] mfChildren = GetComponentsInChildren<MeshFilter>();
    5.  
      CombineInstance[] combine = new CombineInstance[mfChildren.Length];
    6.  
       
    7.  
       
    8.  
      MeshRenderer[] mrChildren = GetComponentsInChildren<MeshRenderer>();
    9.  
      Material[] materials = new Material[mrChildren.Length];
    10.  
       
    11.  
       
    12.  
      MeshRenderer mrSelf = gameObject.AddComponent<MeshRenderer>();
    13.  
      MeshFilter mfSelf = gameObject.AddComponent<MeshFilter>();
    14.  
       
    15.  
       
    16.  
      Texture2D[] textures = new Texture2D[mrChildren.Length];
    17.  
      for (int i = 0; i < mrChildren.Length; i++)
    18.  
      {
    19.  
      if (mrChildren[i].transform == transform)
    20.  
      {
    21.  
      continue;
    22.  
      }
    23.  
      materials[i] = mrChildren[i].sharedMaterial;
    24.  
      Texture2D tx = materials[i].GetTexture("_MainTex") as Texture2D;
    25.  
       
    26.  
       
    27.  
      Texture2D tx2D = new Texture2D(tx.width, tx.height, TextureFormat.ARGB32, false);
    28.  
      tx2D.SetPixels(tx.GetPixels(0, 0, tx.width, tx.height));
    29.  
      tx2D.Apply();
    30.  
      textures[i] = tx2D;
    31.  
      }
    32.  
       
    33.  
       
    34.  
      Material materialNew = new Material(materials[0].shader);
    35.  
      materialNew.CopyPropertiesFromMaterial(materials[0]);
    36.  
      mrSelf.sharedMaterial = materialNew;
    37.  
       
    38.  
       
    39.  
      Texture2D texture = new Texture2D(1024, 1024);
    40.  
      materialNew.SetTexture("_MainTex", texture);
    41.  
      Rect[] rects = texture.PackTextures(textures, 10, 1024);
    42.  
       
    43.  
       
    44.  
      for (int i = 0; i < mfChildren.Length; i++)
    45.  
      {
    46.  
      if (mfChildren[i].transform == transform)
    47.  
      {
    48.  
      continue;
    49.  
      }
    50.  
      Rect rect = rects[i];
    51.  
       
    52.  
       
    53.  
      Mesh meshCombine = mfChildren[i].mesh;
    54.  
      Vector2[] uvs = new Vector2[meshCombine.uv.Length];
    55.  
      //把网格的uv根据贴图的rect刷一遍
    56.  
      for (int j = 0; j < uvs.Length; j++)
    57.  
      {
    58.  
      uvs[j].x = rect.x + meshCombine.uv[j].x * rect.width;
    59.  
      uvs[j].y = rect.y + meshCombine.uv[j].y * rect.height;
    60.  
      }
    61.  
      meshCombine.uv = uvs;
    62.  
      combine[i].mesh = meshCombine;
    63.  
      combine[i].transform = mfChildren[i].transform.localToWorldMatrix;
    64.  
      mfChildren[i].gameObject.SetActive(false);
    65.  
      }
    66.  
       
    67.  
       
    68.  
      Mesh newMesh = new Mesh();
    69.  
      newMesh.CombineMeshes(combine, true,true);//合并网格
    70.  
      mfSelf.mesh = newMesh;
    71.  
      }

    合并前的drawcall:

    合并后的drawcall:

    合并好的网格:

    合并好的贴图:

    下面我们将讨论SkinnedMeshRenderer的合并。

    SkinnedMeshRenderer比MeshRenderer稍微麻烦一点,因为SkinnedMeshRenderer要处理bones。

    以下是步骤:

    1.合并所有材质球所携带的贴图,新建一个材质球,并把合并好的贴图赋予新的材质球。

    2.记录下每个被合并的贴图所处于新贴图的Rect,用一个Rect[]数组存下来。

    3.记录下需要合并的SkinnedMeshRenderer的bones。

    4.合并网格,并把需要合并的各个网格的uv,根据第2步得到的Rect[]刷一遍。

    5.把合并好的网格赋予新的SkinnedMeshRenderer,并把第3步记录下的bones赋予新的SkinnedMeshRenderer。

    6.把新的材质球赋予合并好的网格,此时就只占有1个drawcall了。

    上面红色部分的步骤就是与MeshRenderer不同之处。

    下面是关键代码:

    1.  
      void CombineMesh()
    2.  
      {
    3.  
      SkinnedMeshRenderer[] smrs = GetComponentsInChildren<SkinnedMeshRenderer>();
    4.  
      CombineInstance[] combine = new CombineInstance[smrs.Length];
    5.  
      Material[] materials = new Material[smrs.Length];
    6.  
      Texture2D[] textures = new Texture2D[smrs.Length];
    7.  
       
    8.  
      SkinnedMeshRenderer smrCombine = combineMesh.gameObject.AddComponent<SkinnedMeshRenderer>();
    9.  
      smrCombine.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
    10.  
      smrCombine.receiveShadows = false;
    11.  
       
    12.  
      for (int i = 0; i < smrs.Length; i++)
    13.  
      {
    14.  
      materials[i] = smrs[i].sharedMaterial;
    15.  
      Texture2D tx = materials[i].GetTexture("_MainTex") as Texture2D;
    16.  
       
    17.  
      Texture2D tx2D = new Texture2D(tx.width, tx.height, TextureFormat.ARGB32, false);
    18.  
      tx2D.SetPixels(tx.GetPixels(0, 0, tx.width, tx.height));
    19.  
      tx2D.Apply();
    20.  
      textures[i] = tx2D;
    21.  
      }
    22.  
       
    23.  
      Material materialNew = new Material(materials[0].shader);
    24.  
      materialNew.CopyPropertiesFromMaterial(materials[0]);
    25.  
       
    26.  
      Texture2D texture = new Texture2D(1024, 1024);
    27.  
      Rect[] rects = texture.PackTextures(textures, 10, 1024);
    28.  
      materialNew.SetTexture("_MainTex", texture);
    29.  
       
    30.  
      List<Transform> boneTmp = new List<Transform>();
    31.  
       
    32.  
      for (int i = 0; i < smrs.Length; i++)
    33.  
      {
    34.  
      if (smrs[i].transform == transform)
    35.  
      {
    36.  
      continue;
    37.  
      }
    38.  
      Rect rect = rects[i];
    39.  
       
    40.  
      Mesh meshCombine = CreatMeshWithMesh(smrs[i].sharedMesh);
    41.  
      Vector2[] uvs = new Vector2[meshCombine.uv.Length];
    42.  
       
    43.  
      for (int j = 0; j < uvs.Length; j++)
    44.  
      {
    45.  
      uvs[j].x = rect.x + meshCombine.uv[j].x * rect.width;
    46.  
      uvs[j].y = rect.y + meshCombine.uv[j].y * rect.height;
    47.  
      }
    48.  
       
    49.  
      boneTmp.AddRange(smrs[i].bones);
    50.  
       
    51.  
      meshCombine.uv = uvs;
    52.  
      combine[i].mesh = meshCombine;
    53.  
      combine[i].transform = smrs[i].transform.localToWorldMatrix;
    54.  
      GameObject.Destroy(smrs[i].gameObject);
    55.  
      }
    56.  
       
    57.  
      Mesh newMesh = new Mesh();
    58.  
      newMesh.CombineMeshes(combine, true, true);
    59.  
       
    60.  
      smrCombine.bones = boneTmp.ToArray();
    61.  
      smrCombine.rootBone = rootBone;
    62.  
      smrCombine.sharedMesh = newMesh;
    63.  
      smrCombine.sharedMaterial = materialNew;
    64.  
      }
    65.  
      Mesh CreatMeshWithMesh(Mesh mesh)
    66.  
      {
    67.  
      Mesh mTmp = new Mesh();
    68.  
      mTmp.vertices = mesh.vertices;
    69.  
      mTmp.name = mesh.name;
    70.  
      mTmp.uv = mesh.uv;
    71.  
      mTmp.uv2 = mesh.uv2;
    72.  
      mTmp.uv2 = mesh.uv2;
    73.  
      mTmp.bindposes = mesh.bindposes;
    74.  
      mTmp.boneWeights = mesh.boneWeights;
    75.  
      mTmp.bounds = mesh.bounds;
    76.  
      mTmp.colors = mesh.colors;
    77.  
      mTmp.colors32 = mesh.colors32;
    78.  
      mTmp.normals = mesh.normals;
    79.  
      mTmp.subMeshCount = mesh.subMeshCount;
    80.  
      mTmp.tangents = mesh.tangents;
    81.  
      mTmp.triangles = mesh.triangles;
    82.  
       
    83.  
      return mTmp;
    84.  
      }

    未合并前,有两个网格,占用2drawcall,分别为人物和刀的网格和材质球。



    合并后,只占用 1 drawcall了,而且AnimationController以及其动画能够正常工作:

    合并好的网格:

    合并好的贴图:

    以上便是合并Mesh和Material的内容,目前只关注实现,还可以进一步优化,有以下几点要注意的:

    1.合并的材质球需要使用同一个shader,如果多个材质球使用了不同shader,就要做进一步的出分类处理了。

    2.材质球所用的Texture文件的Read/Write Enable选项要打上勾。

    项目的github地址:  https://github.com/arBao/MeshMaterialCombine

    原创文章,转载请注明出处:   http://blog.csdn.net/dardgen2015/article/details/51517860

  • 相关阅读:
    android系统属性获取及设置
    Android Strings.xml To CSV / Excel互转
    android adb命令 抓取系统各种 log
    Android开源日志库Logger的使用
    解决git仓库从http转为ssh所要处理的问题
    PHP中var_dump
    oracle文字与格式字符串不匹配的解决
    Apache服务器和tomcat服务器有什么区别?
    【手把手教你Maven】构建过程
    Spring MVC页面重定向
  • 原文地址:https://www.cnblogs.com/nafio/p/9328419.html
Copyright © 2020-2023  润新知