3D模型
通過3D建模軟件所建出來的點和面,如以三角形為主的點和面,比如人的腦袋一個球,就是由各種各樣的三角形組成的點和面。
點和面以及紋理坐標都是通過3D建模軟件建模出來的。
Unity會幫我們把模型的信息存到Mesh里面來,Mesh翻譯成中文是網格。
頂點,三角形,紋理坐標,法線和切線。
3D建模軟件
1:Autodesk 3D Studio Max 支持mac os windows;
2: Autodesk 3D Maya 支持windows
3: Cinema4D 支持mac os windows
4: Blender 開源跨平台的全能三維制作軟件, 支持mac os windows, linux;
5: Cheetah3D: 支持mac os
6: Unity與建模軟件的單位比例:
unity系統單位為m, 建模軟件的m的尺寸大小不一樣,所以導入的時候有差異:
內部米 導入unity后的尺寸/m 與Unity單位的比例關系
3Dmax 1 0.01 100:1
Maya 1 100 1:100
Cinema 4D 1 100 1:100
Light Wave 1 0.01 100:1
網格Mesh
1: Unity提供一個Mesh類,允許腳本來創建和修改,通過Mesh類能生成或修改物體的網格,能做出非常酷炫的物體變形特效;
2: Mesh filter 網格過濾器從資源中拿出網格並將其傳遞給MeshRender,用於繪制, 導入模型的時候,Unity會自動創建一個這樣的組件;
3: Mesh 是網格過濾器實例化的Mesh, Mesh中存儲物體的網格數據的屬性和生成或修改物體網格的方法
4: 點---->頂點數組<Vector3>: 每個頂點的x, y, z坐標。Vector3對象,面與面有共用的頂點,所以為了節約內存,先存頂點,然后再存三角形;
5: 面---->三角形索引數組<int>: Mesh里面每個三角形為一個面,由於面與面的頂點公用,所以,用索引來表示三角形的一個面,可以節約模型內存空間, 0, 1, 2表示一個面,對應的頂點是在頂點數組中的索引,三角形頂點的順序為逆時針為正面,順時針為反面。
6: 頂點法線: 面的法線是與面垂直的線, 嚴格意義上講,點是沒有法線的, 在光照計算的時候,使用法線來進行光照計算,
如果一個面上所有的法線都是一樣,那么光着色也一樣,看起來會很奇怪,所以通過某種算法,把多個面公用的頂點的法線根據算法綜合插值,得到頂點法線;
7: 頂點紋理坐標<Vector2>: 頂點對應的紋理上的UV坐標;
6: 頂點切線<Vector4> 頂點切線,知道有這個東西就行了;
Mesh的重要屬性
(1) vertices 網格頂點數組;
(2) normals 網格的法線數組;
(3) tangents 網格的切線數組;
(4) uv 網格的基礎紋理坐標;
(5) uv2 網格設定的第二個紋理坐標;
(6) bounds 網格的包圍盒;
(7) Colors 網格的頂點顏色數組;
(8) triangles 包含所有三角形的頂點索引數組;
(9) vectexCount 網格中的頂點數量(只讀的);
(10) subMeshCount 子網格的數量,每個材質都有一個獨立的網格列表;
(11) bonesWeights: 每個頂點的骨骼權重;
(12) bindposes: 綁定姿勢,每個索引綁定的姿勢使用具有相同的索引骨骼;
Mesh的重要方法
(1) Clear 清空所有的頂點數據和所有的三角形索引;
(2) RecalculateBounds 重新計算網格的包圍盒;
(3) RecalculateNormals 重新計算網格的法線;
(4) Optimze 顯示優化的網格;
(5) GetTriangles 返回網格的三角形列表;
(6) SetTriangles 為網格設定三角形列表;
(7) CominMeshes組合多個網格到同一個網格;
Mesh修改案例
1: 將模型的Mesh復制給Mesh filter組件的Mesh數據。
2: 講模型的Mesh的模型頂點數和面數增加;
3: 開發思路:
(1) 創建項目,配置目錄,導入模型,材質;
(2) 模型拖入場景樹,去掉其他的組件,只保留Mesh filter,點擊里面的實例查看Mesh;
(3) 創建一個空的節點,加入Mesh filter組件,加入MeshRender組件,關聯好材質;
(4) 創建腳本,掛載到這個空節點上,腳本上有組件Mesh filter,關聯到前面有的Mesh節點;
(5) 賦值頂點,三角形, 法線,切線,紋理坐標, 運行觀察結果;
(6) 插值頂點,法線,切線, 紋理坐標, 重新設置三角形索引, 運行觀察結果;
Mesh案例詳細步驟
1.創建Unity工程和文件目錄
2.導入模型和材質到res文件夾下zhang.FBX和wenli.tga(第54)
3.把模型拖入場景中,點擊模型的Mesh Filter組件的Mesh屬性,發現多一個資源出來,那個就是過濾讀取到的網格,可以查看詳細的網格屬性
24個頂點,12個三角形
4.模型的Mesh Renderer組件是用來繪制網格的組件,它的Mesh是Mesh Filter傳遞過來的,如果隱藏這個組件,場景中就不會顯示出模型
5.創建一個材質,把wenli.tga當做材質的紋理貼圖拖進Albedo里面,然后把模型和材質關聯。
6.效果
代碼獲得Mesh
1.創建一個空節點item,添加一個Mesh Filter組件
2.創建一個腳本mesh_test,掛載在item下面,通過代碼來獲得其他模型的Mesh
打開mesh_test
using UnityEngine; using System.Collections;public class mesh_test : MonoBehaviour { public MeshFilter cube_mesh;//獲得編輯器傳遞進來的模型的MeshFilter組件,必須是已經有MeshFilter組件和Mesh的節點 // Use this for initialization void Start () { Mesh cube = this.cube_mesh.mesh;//傳遞進來的模型的MeshFilter組件的Mesh賦值給Mesh類型的變量cube Mesh self_mesh = this.GetComponent<MeshFilter>().mesh;//獲得自己節點下的MeshFilter組件過濾得到的Mesh self_mesh.Clear();//先把自己的Mesh清零 self_mesh.vertices = cube.vertices;//把變量cube的頂點數組傳遞給自己 self_mesh.triangles = cube.triangles;//把變量cube的三角形數組傳遞給自己 self_mesh.normals = cube.normals;//把變量cube的法線數組傳遞給自己 self_mesh.uv = cube.uv;//把變量cube的紋理坐標數組傳遞給自己 self_mesh.tangents = cube.tangents;//把變量cube的切線數組傳遞給自己 self_mesh.RecalculateBounds();//重新計算自己的Mesh } // Update is called once per frame void Update () { } }
3.再給item添加Mesh Renderer組件,再關聯一個材質,這樣,它就可以在場景中繪制出模型了,它的Mesh是別人那里拿的。
復雜操作Mesh
1.思路
把模型中的所有三角形都再增加三個頂點,每個頂點在對應邊的中點。
2.創建一個腳本mesh_test,掛載在item下面,通過代碼來增加頂點
打開腳本mesh_test
using UnityEngine; using System.Collections; using System.Collections.Generic; public class mesh_test : MonoBehaviour { public MeshFilter cube_mesh; // Use this for initialization void Start () { Mesh cube = this.cube_mesh.mesh; //定義需要用到的和Mesh有關的變量 List<Vector3> vertices = new List<Vector3>(); List<int> triangles = new List<int>(); List<Vector3> normals = new List<Vector3>(); List<Vector2> uv = new List<Vector2>(); List<Vector4> tangents = new List<Vector4>(); //遍歷Mesh的三角形數組 for (int i = 0; i < cube.triangles.Length / 3; i++) {//一個模型包含非常多的三角形,每個三角形都要執行我們定義的復雜操作 Vector3 t0 = cube.vertices[cube.triangles[i * 3 + 0]];//得到第一個頂點的坐標 Vector3 t1 = cube.vertices[cube.triangles[i * 3 + 1]];//得到第二個頂點的坐標 Vector3 t2 = cube.vertices[cube.triangles[i * 3 + 2]];//得到第三個頂點的坐標 Vector3 t3 = Vector3.Lerp(t0, t1, 0.5f);//第三個點的坐標為第一個點和第二個點的中點 Vector3 t4 = Vector3.Lerp(t1, t2, 0.5f);//第四個點的坐標為第二個點和第三個點的中點 Vector3 t5 = Vector3.Lerp(t0, t2, 0.5f);//第五個點的坐標為第一個點和第三個點的中點 int count = vertices.Count;//獲得初始的大小,等下用這個變量可以表示索引 //插入頂點坐標到頂點數組vertices中,vertices填充完畢 vertices.Add(t0); // 索引為count + 0 vertices.Add(t1); // 索引為count + 1 vertices.Add(t2); // 索引為count + 2 vertices.Add(t3); // 索引為count + 3 vertices.Add(t4); // 索引為count + 4 vertices.Add(t5); // 索引為count + 5 //------------------------------------------------------------- //插入三角形頂點索引到三角形數組triangles中,triangles填充完畢 triangles.Add(count + 0); triangles.Add(count + 3); triangles.Add(count + 5); triangles.Add(count + 3); triangles.Add(count + 1); triangles.Add(count + 4); triangles.Add(count + 4); triangles.Add(count + 2); triangles.Add(count + 5); triangles.Add(count + 3); triangles.Add(count + 4); triangles.Add(count + 5); //------------------------------------------------------------- //和上面獲得頂點坐標的做法一樣,獲得各個normals法線坐標 Vector3 n0 = cube.normals[cube.triangles[i * 3 + 0]]; Vector3 n1 = cube.normals[cube.triangles[i * 3 + 1]]; Vector3 n2 = cube.normals[cube.triangles[i * 3 + 2]]; Vector3 n3 = Vector3.Lerp(n0, n1, 0.5f); Vector3 n4 = Vector3.Lerp(n1, n2, 0.5f); Vector3 n5 = Vector3.Lerp(n0, n2, 0.5f); //插入法線坐標到法線數組normals中,normals填充完畢 normals.Add(n0); normals.Add(n1); normals.Add(n2); normals.Add(n3); normals.Add(n4); normals.Add(n5); //------------------------------------------------------------- //和上面獲得頂點坐標的做法一樣,獲得各個uv紋理坐標 Vector2 uv0 = cube.uv[cube.triangles[i * 3 + 0]]; Vector2 uv1 = cube.uv[cube.triangles[i * 3 + 1]]; Vector2 uv2 = cube.uv[cube.triangles[i * 3 + 2]]; Vector2 uv3 = Vector3.Lerp(uv0, uv1, 0.5f); Vector2 uv4 = Vector3.Lerp(uv1, uv2, 0.5f); Vector2 uv5 = Vector3.Lerp(uv0, uv2, 0.5f); //插入紋理坐標到紋理數組uv中,uv填充完畢 uv.Add(uv0); uv.Add(uv1); uv.Add(uv2); uv.Add(uv3); uv.Add(uv4); uv.Add(uv5); //------------------------------------------------------------- //和上面獲得頂點坐標的做法一樣,獲得各個tangents切線坐標 Vector4 tan0 = cube.tangents[cube.triangles[i * 3 + 0]]; Vector4 tan1 = cube.tangents[cube.triangles[i * 3 + 1]]; Vector4 tan2 = cube.tangents[cube.triangles[i * 3 + 2]]; Vector4 tan3 = Vector3.Lerp(tan0, tan1, 0.5f); Vector4 tan4 = Vector3.Lerp(tan1, tan2, 0.5f); Vector4 tan5 = Vector3.Lerp(tan0, tan2, 0.5f); //插入切線坐標到切線數組tangents中,tangents填充完畢 tangents.Add(tan0); tangents.Add(tan1); tangents.Add(tan2); tangents.Add(tan3); tangents.Add(tan4); tangents.Add(tan5); } //傳遞給自己的Mesh並重新繪制網格 Mesh self_mesh = this.GetComponent<MeshFilter>().mesh; self_mesh.Clear(); self_mesh.vertices = vertices.ToArray();//List轉換為Array self_mesh.triangles = triangles.ToArray(); self_mesh.normals = normals.ToArray(); self_mesh.uv = uv.ToArray(); self_mesh.tangents = tangents.ToArray(); self_mesh.RecalculateBounds(); //沒有刪除重復的頂點,有待完善 } // Update is called once per frame void Update () { } }
3.效果