zoukankan      html  css  js  c++  java
  • 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了。

    下面是关键代码:

    [csharp] view plain copy
     
    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不同之处。

    下面是关键代码:

    [csharp] view plain copy
     
    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

  • 相关阅读:
    开放源码的对象关系映射工具ORM.NET 插入数据 Insert/Update Data
    开放源码的对象关系映射工具ORM.NET 快档开发入门 Quick Start
    .NET 动态脚本语言Script.NET 开发指南
    开放源码的对象关系映射工具ORM.NET 删除数据 Deleting Records using ORM.NET
    .NET Remoting过时了吗?为什么公司的项目还是选择用.NET Remoting,而不是WCF?
    开放源码的对象关系映射工具ORM.NET 查看和显示数据 View and Display data using ORM.NET
    开放源码的对象关系映射工具ORM.NET 查询表 调用存储过程 增加自定义代码
    技术人生:坚持,每日一博
    CQRS:CQRS + DDD + MDP 实现快速应用程序开发
    NodeJs:Happy代码生成器,重构了代码,更新了文档,完善了示例,欢迎下载使用
  • 原文地址:https://www.cnblogs.com/nafio/p/9221775.html
Copyright © 2011-2022 走看看