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順序沒錯就好了。
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座標從左上角開始(想象攝像機在立方體內部去判斷),
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 }
即這樣完成分開了Mesh,分別使用不一樣的材質。