Unity中Mesh網格的詳解


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

      

 
分類:  Unity


免責聲明!

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



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