Unity3D官方換裝demo中網格合並研究


為了實現游戲人物外形的定制,專門研究了Unity官方換裝demo

1.基礎
BuildPipeline.BuildAssetBundle  把gameobject生成包,可以用於替換和更新
www.assetBundle  動態載入包
SkinnedMeshRenderer 骨骼模型渲染器,美術導出的時候每個子模型都帶SkinnedMeshRenderer


2.基礎模型生成
基礎模型是從characters目錄下找fbx模型(名稱里面不帶@),如果找到(如female.fbx),則把模型下所有有SkinnedMeshRenderer的gameobject刪除,然后給模型本身加了個SkinnedMeshRenderer。
因為female是帶animation的,所以這樣一個基礎模型就生成了,此模型帶animations, 並且帶一個SkinnedMeshRenderer。


3.子模型包的生成
子模型包是根據前面找到的fbx模型的子模型,主要包括face, eye, shoes, top ,pants, hair幾個部分,子模型包包括三個部分:一個pretab,用到的所有的material, bonenames, 


4.生成過程
生成過程主要在兩個函數:
public GameObject Generate()                                  //生成基礎模型    
public GameObject Generate(GameObject root)         //生成root下的子模型
每一個子模型包對應的類是CharacterElement,整個模型的構成過程,就是集合所有子模型SkinnedMeshRenderer的materials, mesh的CombineInstance,還有bonenames,
SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();
        r.sharedMesh = new Mesh();
        r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
        r.bones = bones.ToArray();
        r.materials = materials.ToArray();
全部賦值到了基礎模型的SkinnedMeshRenderer上



5. SkinedMeshRender:該對象負責網格繪制。主要數據成員包括

var bones : Transform[] 骨骼
var materials : Material[] 材質
var sharedMesh : Mesh 網格
其中Mesh的主要成員是
vertices : Vector3[] 頂點
boneWeights : BoneWeight[] 骨骼權重
boneWeights數組與vertices數組對應,表示對應下標的頂點運動受骨骼影響的權重。BoenWeight結構記錄了骨骼在SkinedMeshRender.bones數組中的索引。


6.網格和材質的對應關系
      一張實際的網格只能施加一個材質。因此,當render所使用的mesh包含多個實際網格(sub mesh),它對每個sub mesh所施加的材質實際上是materials數組中對應下標的材質。
7. 合並網格(CombineMeshes)函數的第二個參數是設置是否將多個子網格合並成一張實際的網格。正如前面所述,一個實際的網格只能施加一個材質, 所以只有被合並的所有網格原來使用的就是同一個材質(即共享材質)時,將它們真正合並才能正確應用材質。否則,應該將該參數置為false,表示不實際合 並這些sub mesh,而是將它們作為被合並后Mesh對象的sub mesh。
四、數組對應問題:網格頂點和骨骼、sub mesh和材質之間的對應都是通過數組下標進行的,所以操作時保證新生成的個數組下標對應關系正確是非常重要的。

這是例子中組合創建模型的主要函數,我將自己理解后的備注添加在里面。

  1. // Creates a character based on the currentConfiguration recycling a
  2. // character base, this way the position and animation of the character
  3. // are not changed.
  4. // 這個函數實際上並沒有將各部分的子網格合並成一張網,而只是將他們合並到
  5. // 同一個Mesh下作為sub mesh。因為一張網格只能用一個材質,只有所有子網格
  6. // 都共享同一個材質時,合並成一張網才能保證材質應用正確。
  7. public GameObject Generate(GameObject root) {
  8.     // The SkinnedMeshRenderers that will make up a character will be
  9.     // combined into one SkinnedMeshRenderers using multiple materials.
  10.     // This will speed up rendering the resulting character.
  11.     List<CombineInstance> combineInstances = new List<CombineInstance>();
  12.     List<Material> materials = new List<Material>();
  13.     List<Transform> bones = new List<Transform>();
  14.     //獲得構成骨架的所有Transform
  15.     Transform[] transforms = root.GetComponentsInChildren<Transform>();
  16.     
  17.     //一次處理構成身體的各部分
  18.     foreach (CharacterElement element in currentConfiguration.Values)
  19.     {
  20.         //GetSkinnedMeshRenderer()內部Instantiat了一個由該部分肢體Assets構成的
  21.         //GameObject,並返回Unity自動為其創建SinkedMeshRender。
  22.         SkinnedMeshRenderer smr = element.GetSkinnedMeshRenderer();
  23.         //注意smr.materials中包含的材質數量和順序與下面的sub mesh是對應的
  24.         materials.AddRange(smr.materials);
  25.         for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
  26.         {
  27.             CombineInstance ci = new CombineInstance();
  28.             ci.mesh = smr.sharedMesh;
  29.             ci.subMeshIndex = sub;
  30.             combineInstances.Add(ci);
  31.         }
  32.         // As the SkinnedMeshRenders are stored in assetbundles that do not
  33.         // contain their bones (those are stored in the characterbase assetbundles)
  34.         // we need to collect references to the bones we are using
  35.         // 網格點與骨骼的對應關系是通過Mesh數據結構中的BoneWeight數組來實現的。該數組
  36.         // 與網格頂點數組對應,記錄了每個網格點受骨骼(骨骼記錄在SinkedMeshRender的bones
  37.         // 數組中,按下標索引)影響的權重。
  38.         // 而此處,示例程序提供的肢體Assets並不包含骨骼,而是返回骨骼名稱。因此,推斷
  39.         // GetBoneNames()返回的骨骼名稱應該與實際骨骼數組的順序相同。
  40.         foreach (string bone in element.GetBoneNames())
  41.         {
  42.             foreach (Transform transform in transforms)
  43.             {
  44.                 //通過名字找到實際的骨骼
  45.                 if (transform.name != bone) continue;
  46.                 bones.Add(transform);
  47.                 break;
  48.             }
  49.         }
  50.         Object.Destroy(smr.gameObject);
  51.     }
  52.     // Obtain and configure the SkinnedMeshRenderer attached to
  53.     // the character base.
  54.     // 至此,combineInstances、bones和materials三個數組中的數據對應關系是正確的。
  55.     // 合並時,第二個參數是fals,表示保持子網格不變,只不過將它們統一到一個Mesh里
  56.     // 來管理,這樣只需采用一個SkinedMeshRender繪制,效率較高。
  57.     SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();
  58.     r.sharedMesh = new Mesh();
  59.     r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);
  60.     r.bones = bones.ToArray();
  61.     r.materials = materials.ToArray();
  62.     
  63.     return root;
  64. }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM