Unity中動態建立Mesh

什麼是Mesh?

Mesh是指的模型的網格,3D模型是由多邊形拼接而成,而多邊形實際上又是由多個三角形拼接而成的。即一個3D模型的表面實際上是由多個彼此相連的三角面構成。三維空間中,構成這些三角形的點和邊的集合就是Mesh。html

原理

即動態建立一個Mesh,設置三角形和頂點數據,而後賦值給MeshFilter(增長mesh屬性),經過MeshRenderer(增長材質並渲染出Mesh)繪製出來數組

理論基礎:

一、左手座標系和右手座標系ide

咱們的三維座標系,在3dmax裏是右手座標系,而在Unity裏是左手座標系。函數

左手座標系和右手座標系的區別 http://www.cnblogs.com/mythou/p/3327046.htmlspa

二、三邊面如何組成四邊面3d

如圖,左邊是Unity裏的左手座標系,右邊是在此座標系裏生成的一個面以及它的各個點座標。code

012和230這兩個三邊面就組成了一個四邊面。orm

若是我問這個四邊面有幾個頂點,想必你們都會回答4個,其實是6個,012和230這是6個頂點,不一樣面的頂點不公用。htm

要組成2個三邊面能夠有不少種順序,例如012和320、012和03二、023和012等等等blog

可是咱們通常都是按照4個點的順序來畫2個三邊面組成四邊面,因此可選的只有【012和230、230和012】,以及【032和2十、210和032】這兩大類

這兩類畫法有什麼區別呢?細心的童鞋應該已經發現,這兩種方式前者是逆時針,後者是順時針。

這種循環的方向會致使面的法線方向不一樣,而這個法線方向會決定這個面的朝向。

咱們要肯定這個法線方向其實很簡單,上面說了,Unity裏是左手座標系,拿出左手,伸直,拇指與其餘四個指頭垂直,而後四指彎曲,指尖朝向循環的方向,拇指就指向法線的方向。

由此咱們得出結論,要想生成正確的面(法線指向咱們),咱們只能用【032和2十、210和032】

這裏須要注意的一點是,咱們肯定4個點的循環方向,和生成三邊面時的循環方向無關,只要生成三邊面時,用到的前4個點的index順序沒錯就好了。

Mesh的組成部分

1.vertices(頂點數據數組Vector3[])

2.triangles(三角形頂點索引數組,int[])

3.normals(法線向量數組,Vector3[])

4.uv(紋理座標數組,Vector2[])

頂點座標:頂點座標數組存放Mesh的每一個頂點的空間座標,假設某mesh有n個頂點,則vertex的size爲n

法線:法線數組存放mesh每一個頂點的法線,大小與頂點座標對應,normal[i]對應頂點vertex[i]的法線

      法線詳解:

         法線就是垂直於面的一條線,它有方向,沒有大小。

         法線的方向就是面朝外的方向。好比咱們如今盯着顯示器看,從顯示器的正中心會有一條法線垂直於屏幕指向咱們。

         法線向外的面就是正面,相反的就是背面,通常來說,從正面看才能看到面,背面看面是看不到的。

紋理座標:它定義了圖片上每一個點的位置的信息. 這些點與3D模型是相互聯繫的, 以決定表面紋理貼圖的位置. UV就是將圖像上每個點精確對應到模型物體的表面. uv[i]對應vertex[i]

三角形序列:每一個mesh都由多個三角面組成,而三角面的三個點就是頂點座標裏的點,三角形的數組的size = 三角形個數 * 3

      三邊面和四邊面:

        三邊面就是三條邊組成的面,四邊面就是四條邊組成的面。

        三邊面在三維空間中是不可扭曲的,而四邊面在三維空間中能夠扭曲。因此Unity裏只支持三邊面。其餘支持四邊面的軟件例如3dmax在導出fbx的時候,會把四邊面轉換成三邊面。

 

建立一個立方體

1.定頂點座標

通常咱們會以立方體幾何中心爲座標原點。

代碼:

 1         //頂點數組
 2         Vector3[] _vertices = 
 3         {
 4             // front
 5             new Vector3(-5.0f, 10.0f, -5.0f),
 6             new Vector3(-5.0f, 0.0f, -5.0f),
 7             new Vector3(5.0f, 0.0f, -5.0f),
 8             new Vector3(5.0f, 10.0f, -5.0f),
 9 
10 
11             // left  
12             new Vector3(-5.0f, 10.0f, -5.0f),
13             new Vector3(-5.0f, 0.0f, -5.0f),
14             new Vector3(-5.0f, 0.0f, 5.0f),//
15             new Vector3(-5.0f, 10.0f, 5.0f),
16 
17             // back
18             new Vector3(-5.0f, 10.0f, 5.0f),
19             new Vector3(-5.0f, 0.0f, 5.0f),
20             new Vector3(5.0f, 0.0f, 5.0f),
21             new Vector3(5.0f, 10.0f, 5.0f),
22 
23 
24             // right  
25             new Vector3(5.0f, 10.0f, 5.0f),
26             new Vector3(5.0f, 0.0f, 5.0f),
27             new Vector3(5.0f, 0.0f, -5.0f),
28             new Vector3(5.0f, 10.0f, -5.0f),
29 
30 
31             // Top
32             new Vector3(-5.0f, 10.0f, 5.0f),
33             new Vector3(5.0f, 10.0f, 5.0f),
34             new Vector3(5.0f, 10.0f, -5.0f),
35             new Vector3(-5.0f, 10.0f, -5.0f),
36 
37            // Bottom
38             new Vector3(-5.0f, 0.0f, 5.0f),
39             new Vector3(5.0f, 0.0f, 5.0f),
40             new Vector3(5.0f, 0.0f, -5.0f),
41             new Vector3(-5.0f, 0.0f, -5.0f),
42 
43         };

這裏有人會有疑問,正方體6個面,每一個面由2個三角形組成,因此共須要36個三角形頂點索引。可是正方體只有8個頂點,爲何須要24個頂點座標數據呢?

答案是:Unity3D的Mesh.triangles是三角形索引數組,不只依靠這個索引值索引三角形頂點座標,並且索引紋理座標,索引法線向量。即正方體的每一個頂點都參與了3個平面,而這3個平面的法線向量是不一樣的,該頂點在渲染這3個平面的時候須要索引到不一樣的法線向量。而因爲頂點座標和法線向量是由同一個索引值triangles[Index]取得的,例如,有三個點在vertices中索引到的頂點都爲(0,0,0),可是在normals中索引到的法向量值各不相同。這就決定了在正方體中一個頂點,須要有3份存儲。(若是你須要建立其它模型,須要根據實際狀況決定頂點座標的冗餘度。實質上頂點座標的冗餘正是方便了法線座標、紋理座標的存取。),通常不共點。還有就是Unity中是左手座標系,必定記好,由於在繪製三角面時很重要。

2.三角面索引

     //索引數組
        int[] _triangles =
        {
          //front
          2,1,0,
          0,3,2,
          //left
          4,5,6,
          4,6,7,
          //back
          9,11,8,
          9,10,11,
          //right
          12,13,14,
          12,14,15,
          ////up
          //16,17,18,
          //16,18,19,
          ////buttom
          //21,23,22,
          //21,20,23,

          //不可跳躍設置索引值(不然會提示一些索引超出邊界頂點   15直接20不可,要連續15-16)
          17,19,18,
          17,16,19,
        };

這裏設置的原則時外面被渲染裏面剔除掉,順時針構建(注意裏外面的區別),還要注意的一個點,如上所寫,好比我想生成5個面,那你的索引值也要是連續的,不可16直接蹦到20。這裏立法體面的繪製順序是(即繪製三角面的面與上面頂點順序要一致)設置頂點的順序

3.UV座標

代碼:

 1      //UV數組
 2         Vector2[] uvs =
 3         {
 4             // Front
 5             new Vector2(1.0f, 0.0f),
 6             new Vector2(1.0f, 1.0f),
 7             new Vector2(1.0f, 0.0f),
 8             new Vector2(0.0f, 0.0f),
 9 
10             
11             // Left
12             new Vector2(1.0f, 1.0f),
13             new Vector2(0.0f, 1.0f),
14             new Vector2(0.0f, 0.0f),
15             new Vector2(1.0f, 0.0f),
16 
17             
18             // Back
19             new Vector2(1.0f, 0.0f),
20             new Vector2(1.0f, 1.0f),
21             new Vector2(1.0f, 0.0f),
22             new Vector2(0.0f, 0.0f),
23 
24             
25             // Right
26             new Vector2(1.0f, 1.0f),
27             new Vector2(0.0f, 1.0f),
28             new Vector2(0.0f, 0.0f),
29             new Vector2(1.0f, 0.0f),
30 
31             //// Top
32             //new Vector2(0.0f, 0.0f),
33             //new Vector2(1.0f, 0.0f),
34             //new Vector2(1.0f, 1.0f),
35             //new Vector2(0.0f, 1.0f),
36 
37 
38             // Bottom
39             new Vector2(0.0f, 0.0f),
40             new Vector2(1.0f, 0.0f),
41             new Vector2(1.0f, 1.0f),
42             new Vector2(0.0f, 1.0f),
43 
44         };

UV座標從左上角開始(想象攝像機在立方體內部去判斷),

開始的即(0,0),通常是在0-1之間,一些比較大的面爲防止紋理被拉伸馬賽克,咱們會重複貼紋理,會有大於1的狀況,這裏的點要與頂點座標一一對應。重複貼紋理時須要將重複帖的貼圖的Wrap Mode設爲Repeat(重複)。
即:
4.構建mesh
代碼:
 1         Mesh mesh = new Mesh()
 2         {
 3             vertices = _vertices,
 4             uv = uvs,
 5             triangles = _triangles,
 6         };
 7 
 8         //從新計算網格的法線
 9         //在修改完頂點後,一般會更新法線來反映新的變化。法線是根據共享的頂點計算出來的。
10         //導入到網格有時不共享全部的頂點。例如:一個頂點在一個紋理座標的接縫處將會被分紅兩個頂點。
11         //所以這個RecalculateNormals函數將會在紋理座標接縫處建立一個不光滑的法線。
12         //RecalculateNormals不會自動產生切線,所以bumpmap着色器在調用RecalculateNormals以後不會工做。然而你能夠提取你本身的切線。
13         mesh.RecalculateNormals();

給mesh屬性賦值。

5.增長MeshFilter組件,網格過濾。以及增長MeshRenderer組件添加材質實現渲染。OK!!!到這基本已經繪製完了,Mesh已經出來了。

  1 using System.Collections;
  2 using System.Collections.Generic;
  3 using UnityEngine;
  4 
  5 public class ShaderBase : MonoBehaviour
  6 {
  7 
  8     void Start()
  9     {
 10         GameObject gameObject = new GameObject("Cube");
 11         gameObject.transform.position = Vector3.zero;
 12  
 13         //頂點數組
 14         Vector3[] _vertices = 
 15         {
 16             // front
 17             new Vector3(-5.0f, 10.0f, -5.0f),
 18             new Vector3(-5.0f, 0.0f, -5.0f),
 19             new Vector3(5.0f, 0.0f, -5.0f),
 20             new Vector3(5.0f, 10.0f, -5.0f),
 21 
 22 
 23             // left  
 24             new Vector3(-5.0f, 10.0f, -5.0f),
 25             new Vector3(-5.0f, 0.0f, -5.0f),
 26             new Vector3(-5.0f, 0.0f, 5.0f),//
 27             new Vector3(-5.0f, 10.0f, 5.0f),
 28 
 29             // back
 30             new Vector3(-5.0f, 10.0f, 5.0f),
 31             new Vector3(-5.0f, 0.0f, 5.0f),
 32             new Vector3(5.0f, 0.0f, 5.0f),
 33             new Vector3(5.0f, 10.0f, 5.0f),
 34 
 35 
 36             // right  
 37             new Vector3(5.0f, 10.0f, 5.0f),
 38             new Vector3(5.0f, 0.0f, 5.0f),
 39             new Vector3(5.0f, 0.0f, -5.0f),
 40             new Vector3(5.0f, 10.0f, -5.0f),
 41 
 42 
 43             // Top
 44             new Vector3(-5.0f, 10.0f, 5.0f),
 45             new Vector3(5.0f, 10.0f, 5.0f),
 46             new Vector3(5.0f, 10.0f, -5.0f),
 47             new Vector3(-5.0f, 10.0f, -5.0f),
 48 
 49            // Bottom
 50             new Vector3(-5.0f, 0.0f, 5.0f),
 51             new Vector3(5.0f, 0.0f, 5.0f),
 52             new Vector3(5.0f, 0.0f, -5.0f),
 53             new Vector3(-5.0f, 0.0f, -5.0f),
 54 
 55         };
 56         //索引數組
 57         int[] _triangles =
 58         {
 59           //front
 60           2,1,0,
 61           0,3,2,
 62           //left
 63           4,5,6,
 64           4,6,7,
 65           //back
 66           9,11,8,
 67           9,10,11,
 68           //right
 69           12,13,14,
 70           12,14,15,
 71           ////up
 72           //16,17,18,
 73           //16,18,19,
 74           ////buttom
 75           //21,23,22,
 76           //21,20,23,
 77 
 78           //不可跳躍設置索引值(不然會提示一些索引超出邊界頂點   15直接20不可,要連續15-16)
 79           17,19,18,
 80           17,16,19,
 81         };
 82 
 83         //UV數組
 84         Vector2[] uvs =
 85         {
 86             // Front
 87             new Vector2(1.0f, 0.0f),
 88             new Vector2(1.0f, 1.0f),
 89             new Vector2(1.0f, 0.0f),
 90             new Vector2(0.0f, 0.0f),
 91 
 92             
 93             // Left
 94             new Vector2(1.0f, 1.0f),
 95             new Vector2(0.0f, 1.0f),
 96             new Vector2(0.0f, 0.0f),
 97             new Vector2(1.0f, 0.0f),
 98 
 99             
100             // Back
101             new Vector2(1.0f, 0.0f),
102             new Vector2(1.0f, 1.0f),
103             new Vector2(1.0f, 0.0f),
104             new Vector2(0.0f, 0.0f),
105 
106             
107             // Right
108             new Vector2(1.0f, 1.0f),
109             new Vector2(0.0f, 1.0f),
110             new Vector2(0.0f, 0.0f),
111             new Vector2(1.0f, 0.0f),
112 
113             //// Top
114             //new Vector2(0.0f, 0.0f),
115             //new Vector2(1.0f, 0.0f),
116             //new Vector2(1.0f, 1.0f),
117             //new Vector2(0.0f, 1.0f),
118 
119 
120             // Bottom
121             new Vector2(0.0f, 0.0f),
122             new Vector2(1.0f, 0.0f),
123             new Vector2(1.0f, 1.0f),
124             new Vector2(0.0f, 1.0f),
125 
126         };
127 
128         Mesh mesh = new Mesh()
129         {
130             vertices = _vertices,
131             uv = uvs,
132             triangles = _triangles,
133         };
134 
135         //從新計算網格的法線
136         //在修改完頂點後,一般會更新法線來反映新的變化。法線是根據共享的頂點計算出來的。
137         //導入到網格有時不共享全部的頂點。例如:一個頂點在一個紋理座標的接縫處將會被分紅兩個頂點。
138         //所以這個RecalculateNormals函數將會在紋理座標接縫處建立一個不光滑的法線。
139         //RecalculateNormals不會自動產生切線,所以bumpmap着色器在調用RecalculateNormals以後不會工做。然而你能夠提取你本身的切線。
140         mesh.RecalculateNormals();
141         gameObject.AddComponent<MeshFilter>().mesh=mesh;
142         //Material/New Material 1
143         gameObject.AddComponent<MeshRenderer>().material = Resources.Load<Material>("Material/New Material");
144 
145     }
146 
147 }

 這不是上述代碼的結果圖片,這是動態建立外圍盒的圖片,作法同樣。

 

最新:這個立方體,我想底面和側面貼不一樣貼圖,如何實現?

使用  mesh.subMeshCount = X;即subMesh,子網格,具體使用以下:

 1   Vector3 contralPos = (maxPos + minPos) / 2;
 2             float boxHight = Mathf.Abs(maxPos.y - minPos.y);
 3             float boxLength = Mathf.Abs(maxPos.x - minPos.x);
 4             float boxWidth = Mathf.Abs(maxPos.z - minPos.z);
 5             vertexPosArray = AddVertexPos(1.2f * boxLength, 1.2f * boxWidth, 1.4f * boxHight);
 6             vertexIndexList = AddVertexIndex();
 7             uvArr = SetUVPos(GetIntValue(boxLength / (textureSizeL * uvNorm)), GetIntValue(boxWidth / (textureSizeL * uvNorm)), GetIntValue(boxHight / (textureSizeW * uvNorm)));
 8             Mesh mesh = new Mesh()
 9             {
10                 vertices = vertexPosArray,
11                 uv = uvArr,
12             };
13             mesh.subMeshCount = 2;
14             mesh.SetTriangles(vertexIndexList[0], 0);
15             mesh.SetTriangles(vertexIndexList[1], 1);
16             mesh.RecalculateNormals();
17             GameObject Box = new GameObject(name);
18             // Box.transform.localPosition = contralPos;
19             Box.transform.localPosition = new Vector3(contralPos.x, minPos.y, contralPos.z);
20             Box.AddComponent<MeshFilter>().mesh = mesh;
21             Material[] materials = new Material[2];
22             materials[0] = new Material(Resources.Load<Material>("Materials/Mine/MinefieldTexture_Side"));
23             materials[1] = new Material(Resources.Load<Material>("Materials/Mine/MinefieldTexture_Buttom"));
24             Box.AddComponent<MeshRenderer>().materials = materials;
 mesh.subMeshCount = 2;
 mesh.SetTriangles(vertexIndexList[0], 0); mesh.SetTriangles(vertexIndexList[1], 1);
這是指定子網格對應的索引集合,在設置索引時,應該這樣分開存儲:
 1  /// <summary>
 2         /// 添加索引
 3         /// </summary>
 4          private List<int[]> AddVertexIndex()
 5          {
 6             List<int[]> indexList = new List<int[]>();
 7             int[] sideIndexArray =
 8             {
 9                //front
10                2,1,0,
11                2,0,3,
12 
13                //back
14                4,5,6,
15                4,6,7,
16 
17                //left
18                8,10,11,
19                8,9,10,
20 
21                //right
22                13,15,14,
23                13,12,15,
24             };
25             int[] buttomFaceIndexArray =
26             {
27                //buttom
28                17,16,19,
29                17,19,18
30             };
31             indexList.Add(sideIndexArray);
32             indexList.Add(buttomFaceIndexArray);
33 
34             return indexList;
35         }
View Code

即這樣完成分開了Mesh,分別使用不一樣的材質。

相關文章
相關標籤/搜索