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.效果

