1. 程式人生 > 程式設計 >遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

目錄
  • 一、前言
  • 二、Hello Mesh
  • 三、萌新初識Mesh
    • 1、引擎內建的Mesh
    • 2、Mesh是什麼
  • 三、Mesh的建立方式
    • 1、第三方建模軟體
    • 2、Unity建模外掛:ProBuilder
    • 3、程式動態生成網格
  • 四、Unity中如何顯示網格
    • 1、MeshFilter:網格過濾器
    • 2、MeshRenderer:網格渲染器
    • 3、SkinnedMeshRenderer:蒙皮網格渲染器
      • 3.1 骨骼動畫
      • 3.2 SkinnedMeshRenderer元件
      • 3.2 使用BakeMesh進行優化
  • 五、純程式碼動態建立網格
    • 1、建立Mesh物件
      • 2、頂點座標
        • 3、UV座標
          • 4、三角形序列
            • 5、重新計演算法線和包圍體
              • 6、完整版程式碼
                • 7、測試
                  • 8、專案原始碼
                  • 六、網格相關的開源專案
                    • 1、2D網格塗鴉
                      • 2、3D網格塗鴉
                        • 3、網格體素化
                          • 4、網格平滑演算法
                            • 5、網格切割
                              • 6、網格合併
                              • 七、未完的探險

                                一、前言

                                嗨,大家好,我是新發。
                                有同學私信我讓我寫一篇Unity網格相關的教程,

                                在這裡插入圖片描述

                                那我就帶大家來一次Unity的網格探險之旅吧~

                                二、Hello Mesh

                                我揹著旅行揹包走在Unity的場景中,突然眼前出現了一棵樹,

                                在這裡插入圖片描述

                                我走近一看,這棵樹身上掛著MeshFilterMeshRenderer元件,根據Unity探險手冊記載,這個MeshFilter是網格過濾器,它會引用一個網格資源,我順騰摸瓜,找到了對應的網格,

                                在這裡插入圖片描述

                                實在太美了,我久久佇立,這就是網格啊!
                                正當我欣賞著網格三角形時,突然世界暗了下來,眼前出現了一團火,

                                在這裡插入圖片描述

                                我又拿出了Unity探險手冊,啊,這一定就是粒子系統了!它可以動態生成網格。

                                在這裡插入圖片描述

                                天外傳來一陣打字聲,場景中出現了一行看起來像文字的網格,作為一個具有多年Hello World經驗的程式設計師,我看出了第一個單詞應該是Hello,第二個單詞…我知道了,

                                在這裡插入圖片描述

                                Hello Mesh

                                在這裡插入圖片描述

                                (此處為震撼人心的入場音樂)

                                三、萌新初識Mesh

                                1、引擎內建的Mesh

                                網格的英文名是MeshUnity萌新最先接觸的網格應該就是引擎內建的Cube(正方體)、Capsule(膠囊體)、Cylinder(圓柱體)、Plane(平面)、Sphere(球體)、Quad(四邊形),如下

                                在這裡插入圖片描述

                                事實上,我們在Unity

                                場景中,所有能被渲染出來的物體都會帶有網格,比如3D模型、粒子特效、UI、文字等等。

                                2、Mesh是什麼

                                從概念上講,網格是圖形硬體用來繪製複雜內容的構造。它至少包含一組定義3D空間中點的頂點,以及一組連線這些點的三角形,實際上還包含法線、頂點顏色紋理座標等資訊,這些三角形構成了網格所代表的任何表面。

                                我們可以看下UnityMesh類,Mesh的屬性和方法很多,我這裡列舉幾個比較常用的,如下

                                // 頂點座標陣列
                                public Vector3[] vertices { get; set; }
                                // 法線向量陣列
                                public Vector3[] normals { get; set; }
                                // 頂點顏色陣列
                                public Color[] colors { get; set; }
                                // 三角形序列陣列,每三個數字為一組
                                public int[] triangles { get; set; }
                                // uv座標陣列
                                public Vector2[] uv { get; set; }
                                // 重新計演算法線,在修改完頂點後,通常會更新法線來反映新的變化,注意,法線是根據共享的頂點計算出來的。
                                public void RecalculateNormals();
                                // 從法線和紋理座標重新計算網格的切線。修改網格的頂點和法線之後,如果網格使用引用法線貼圖的著色器進行渲染,則切線需要更新。 
                                public void RecalculateTangents();
                                // 重新計算從網格包圍體的頂點,在修改頂點後需要這個函式以確保包圍體是正確的,賦值三角形將自動重新計算這個包圍體。
                                public void RecalculateBounds();
                                

                                畫個圖,方便大家有個直觀印象,

                                在這裡插入圖片描述

                                三、Mesh的建立方式

                                1、第三方建模軟體

                                建模本質上就是建網格,我們可以事先通過第三方建模軟體來建立模型網格,

                                在這裡插入圖片描述

                                常見的建模軟體比如

                                在這裡插入圖片描述

                                3DS MAX官網:https://www.autodesk.com/products/3ds-max/overview

                                在這裡插入圖片描述

                                MAYA官網:https://www.autodesk.com/products/maya/overview

                                在這裡插入圖片描述

                                blender官網:https://www.blender.org/

                                2、Unity建模外掛:ProBuilder

                                Unity官方提供了一個可以用來建立和自定義幾何體的工具ProBuilder,我們可以在UnityPackage Manager中下載到這個外掛,

                                在這裡插入圖片描述

                                使用ProBuilder我們可以直接在Unity中建立或編輯簡單的幾何體,不用通過第三方建模軟體,提升了效率,方便快速搭建場景原型,

                                在這裡插入圖片描述

                                3、程式動態生成網格

                                網格也可以是程式動態生成的,比如粒子系統的網格就是動態生成的,

                                在這裡插入圖片描述

                                又比如文字,也是程式動態生成網格,

                                在這裡插入圖片描述

                                文章後面我還會手把手教你如何使用純程式碼來構建網格,這裡先不急著寫程式碼,我們繼續探尋網格的祕密先~

                                四、Unity中如何顯示網格

                                Unity中,我們要顯示一個網格,需要用到兩個元件:MeshFilterMeshRenderer

                                注:你也可以直接使用SkinnedMeshRenderer元件,與MeshFilterMeshRenderer的區別我下文會講。

                                1、MeshFilter:網格過濾器

                                MeshFilter是網格過濾器,我們需要通過它設定引用的網格資源,比如這裡引用的是一個Cube(正方體)網格。

                                在這裡插入圖片描述

                                我們可以看下MeshFilter.cs的原始碼,

                                [RequireComponent(typeof(Transform))]
                                [NativeHeader("Runtime/Graphics/Mesh/MeshFilter.h")]
                                public sealed partial class MeshFilter : Component
                                {
                                    [RequiredByNativeCode]  // MeshFilter is used in the VR Splash screen.
                                    private void DontStripMeshFilter() {}
                                
                                    extern public Mesh sharedMesh { get; set; }
                                    extern public Mesh mesh {[NativeName("GetInstantiatedMeshFromScript")] get; [NativeName("SetInstantiatedMesh")] set; }
                                }
                                

                                MeshFilter只有兩個屬性:meshsharedMesh
                                我們檢視Unity的官方手冊,看看meshsharedMesh的區別:https://docs.unity3d.com/ScriptReference/MeshFilter.html

                                在這裡插入圖片描述

                                我來解讀一下,mesh訪問的是一個Mesh資源的例項(副本),這意味著我們修改這個mesh並不會修改到原始資源本身,改的只是Mesh的例項(副本)。
                                sharedMesh是原始資源的引用,如果修改了sharedMesh,比如修改頂點座標,那麼原始資源也會被修改。
                                畫成圖大概是這樣子:

                                在這裡插入圖片描述

                                這裡我順手寫個隨機修改Mesh頂點座標的,如下,將下面這個RandoMeshmVertices指令碼掛到MeshFilter元件所在的物體上即可,

                                // RandoMeshmVertices.cs
                                // 隨機修改Mesh頂點座標
                                using UnityEngine;
                                public class RandoMeshmVertices: MonoBehaviour
                                {
                                    // Mesh的例項
                                    MeshFilter meshFilter;
                                    // 頂點的原始座標
                                    Vector3[] originalVertices;
                                    void Start()
                                    {
                                        meshFilter = GetComponent<MeshFilter>();
                                        originalVertices = meshFilter.mesh.vertices;
                                    }
                                    void Update()
                                    {
                                        // 隨機修改頂點座標
                                        Vector3[] vertices = meshFilter.mesh.vertices;
                                        for (int i = 0,len = originalVertices.Length; i < len; ++i)
                                        {
                                            var v = originalVertices[i];
                                            vertices[i] = v + Random.Range(-0.1F,0.1F) * Vector3.one;
                                        }
                                        meshFilter.mesh.vertices = vertices;
                                        meshFilter.mesh.RecalculateNormals();
                                    }
                                }
                                

                                執行效果如下,網格頂點座標發生了隨機偏移,

                                在這裡插入圖片描述

                                關於mesh屬性的訪問需要特別注意一下,我們先看看Unity官方手冊的說明,https://docs.unity3d.com/ScriptReference/MeshFilter-mesh.html

                                在這裡插入圖片描述

                                翻譯一下就是,如果一個Mesh資源已經被分配給MeshFiltermesh屬性,那麼當我們在程式碼中第一次訪問mesh屬性時才正真建立了Mesh的例項;再次訪問mesh屬性時則直接返回這個例項,並且一旦mesh屬性被訪問,則與原始共享網格的連結會丟失,此時sharedMesh變成mesh的別名,如果我們想避免這種自動生成Mesh例項,可以使用sharedMesh代替。
                                寫成虛擬碼的話大致是這樣子:

                                public class MeshFilter ...
                                {
                                	...
                                	private Mesh _mesh;
                                	public Mesh mesh
                                	{
                                		get
                                		{
                                			if (_mesh == null) 
                                			{
                                				_mesh = new Mesh();
                                				Copy(sharedMeh,_mesh);
                                			}
                                			return _mesh;
                                		}
                                	}
                                	...
                                }
                                

                                還有,如果我們訪問了mesh屬性而導致自動建立了Mesh例項,則需要在程式碼中主動呼叫Resources.UnloadUnusedAssets來銷燬沒有引用的Mesh例項,建議是在場景切換時呼叫Resources.UnloadUnusedAssets

                                2、MeshRenderer:網格渲染器

                                MeshRenderer,顧名思義,網格渲染器。我們依舊先來看看官方手冊的介紹:

                                https://docs.unity3d.com/Manual/class-MeshRenderer.html

                                在這裡插入圖片描述

                                翻譯過來就是MeshRenderer會從MeshFilter那裡拿到網格資料並在所在物體的位置處將其渲染出來。
                                如果沒有MeshRenderer,我們就看不見網格了,如下

                                在這裡插入圖片描述

                                另外,我們還需要在MeshRendererMaterials中指定一個材質球,這樣才能正常顯示,否則模型表面就是紫色的。

                                在這裡插入圖片描述

                                3、SkinnedMeshRenderer:蒙皮網格渲染器

                                SkinnedMeshRenderer是蒙皮網格渲染器,可能有小夥伴就會問了,上面使用MeshFilterMeshRenderer已經可以顯示模型網格了,為什麼又弄了一個SkinnedMeshRenderer呢?
                                看下Unity官方手冊的介紹:https://docs.unity3d.com/Manual/class-SkinnedMeshRenderer.html

                                在這裡插入圖片描述

                                可以看到SkinnedMeshRenderer其實是針對帶 骨骼動畫 的模型的渲染的。

                                3.1 骨骼動畫

                                為什麼需要做骨骼動畫呢?

                                就好比我們人一樣,我們的骨骼會隨著我們肌肉的伸縮而動,骨骼又可以帶動它管轄的身體部位發生形變和移動,骨骼還會影響它所連線的其他骨骼一起發生聯動。對應到模型動作上,想想一個簡單的舉手動作要牽涉到多少網格頂點的移動,如果沒有骨骼,那動畫師要每幀挨個網格頂點進行調整,即使動畫做出來了,這個動畫也不能複用到其他模型上,因為不同模型的頂點資訊都不一樣,這麼低效的動畫製作肯定是不行的,於是,就有了骨骼動畫。

                                骨骼動畫的原理

                                就是將模型分為骨骼(Bone)和蒙皮(Mesh)兩個部分,骨骼可分為多層父子骨骼,每個骨骼都附加到周圍網格的一些頂點上,在動畫關鍵幀資料的驅動下,計算出各個父子骨骼的位置,基於骨骼的控制通過頂點混合動態計算出蒙皮網格的頂點。

                                動畫師可以在MAYA軟體上給模型繫結骨骼,繫結骨骼不是本文的重點,這裡就不展講開具體操作了,感興趣的同學可以自行百科學習。

                                製作好匯出為fbx格式,

                                在這裡插入圖片描述

                                fbx檔案匯入到Unity中,選中它,

                                在這裡插入圖片描述

                                Inspector檢視中點選Rig按鈕,

                                在這裡插入圖片描述

                                我們可以看到動畫型別Animation TypeNoneLegacyGenericHumanoid四個,

                                在這裡插入圖片描述

                                具體選項可以參見Unity官方手冊:https://docs.unity3d.com/Manual/FBXImporter-Rig.html

                                在這裡插入圖片描述

                                我這裡演示一下人形骨骼動畫,選擇Humanoid型別,Avatar Definition選擇Create From This Model,然後點選Configure

                                在這裡插入圖片描述

                                Inspector檢視中我們就可以看到對應的骨骼繫結資訊了,

                                在這裡插入圖片描述

                                如下,綠色的線段就是一根根骨骼,

                                在這裡插入圖片描述

                                我們調整一根骨骼,對應的網格也會跟著一起動,如下

                                在這裡插入圖片描述

                                這樣做出來的人形動畫是可以進行復用了,有請妹子上場,

                                在這裡插入圖片描述

                                骨骼動畫資源的話,我在之前的文章中也介紹過一個寶藏Mixamo:https://www.mixamo.com/,上面有很多做好的人形骨骼動畫,

                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

                                看,是不是挺好玩的,

                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

                                我們可以把它的動作直接複用到我們自己的人形模型上,效果如下:

                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)
                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)
                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

                                3.2 SkinnedMeshRenderer元件

                                骨骼動畫可以正常播放,要歸功於SkinnedMeshRenderer元件,製作好骨骼動畫的fbx檔案匯入Unity中,Unity會自動幫我們掛上SkinnedMeshRenderer元件,

                                在這裡插入圖片描述

                                其中幾個重要的屬性我講一下,
                                Bounds:骨骼資料;
                                Mesh:要渲染的網格;
                                Root Bone:根骨骼,其他骨骼都是相對根骨骼移動的;
                                BlendShapes:一般用於製作表情融合,我之前寫過一篇文章講過BlendShapes

                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

                                我們再來看看SkinnedMeshRenderer指令碼的屬性和方法:

                                在這裡插入圖片描述

                                需要講的應該就是這個BakeMesh方法了,下面我就單獨拎出來講下BakeMesh

                                3.2 使用BakeMesh進行優化

                                假設現在場景中有100只皮卡丘,每隻皮卡丘的網格、貼圖、動作相同,

                                在這裡插入圖片描述

                                如果每隻皮卡丘身上都掛SkinnedMeshRenderer,那就是100SkinnedMeshRenderer在計算蒙皮,

                                在這裡插入圖片描述

                                由於SkinnedMeshRenderer是根據骨骼動畫動態計算網格頂點座標,這個運算開銷還是不小的,有沒有辦法優化呢?

                                SkinnedMeshRenderer提供了一個BakeMesh方法,可以將一個蒙皮動畫的某個時間點上的動作,Bake成一個不帶蒙皮的Mesh,我們統一使用這個Mesh來顯示其餘的皮卡丘,這樣就可以大大減少了SkinnedMeshRenderer的計算了,
                                畫成圖大概是這樣子:

                                在這裡插入圖片描述

                                不過,上面這種方案的侷限性是每隻皮卡丘的動畫是相同的,如果突然某一隻皮卡丘要播放與其他皮卡丘不同的動畫,那就不行了。

                                另一種Bake方案可以是這樣:
                                對皮卡丘的每個動畫進行遍歷取樣,把取樣到的Mesh存到陣列中,因為這裡要Bake很多網格,比較耗時,建議在載入場景時時就完成取樣過程;後面要播放某個動畫時直接從這個Mesh陣列中獲取Mesh來顯示,此時直接使用MeshFilterMeshRenderer的方式來顯示網格就好了。
                                貼個BakeMesh的示例指令碼:

                                using UnityEngine;
                                using System.Collections.Generic;
                                /// <summary>
                                /// Bake Mesh 示例
                                /// </summary>
                                public class BakeMeshTest : MonoBehaviour
                                {
                                    [SerializeField]
                                    Animation m_animation;
                                    [SerializeField]
                                    SkinnedMeshRenderer m_skinnedMeshRenderer; 
                                    [SerializeField]
                                    string m_clipToBake = "Idle";
                                    List<Mesh> m_bakedMeshList = new List<Mesh>();
                                    /// <summary>
                                    /// 取樣幀數
                                    /// </summary>
                                    [SerializeField]
                                    int m_numFramesToBake = 30;
                                    void Start()
                                    {
                                        // 獲取要Bake的動畫片段
                                        AnimationState clipState = m_animation[m_clipToBake];
                                        if (clipState == null)
                                        {
                                            Debug.LogError(string.Format("Unable to get clip '{0}'",m_clipToBake),this);
                                            return;
                                        }
                                        // 開始播放動畫
                                        m_animation.Play(m_clipToBake,PlayMode.StopAll);
                                        // 設定動畫初始時間戳
                                        clipState.time = 0.0f;
                                        // 取樣幀間隔
                                        float deltaTime = clipState.length / (float)(m_numFramesToBake - 1);
                                        for (int frameIndex = 0; frameIndex < m_numFramesToBake; ++frameIndex)
                                        {
                                            string frameName = string.Format("BakedFrame{0}",frameIndex);
                                            // 建立Mesh
                                            Mesh frameMesh = new Mesh();
                                            frameMesh.name = frameName;
                                            // 動畫取樣
                                            m_animation.Sample();
                                            // 執行BakeMesh
                                            m_skinnedMeshRenderer.BakeMesh(frameMesh);
                                            m_bakedMeshList.Add(frameMesh);
                                            // 設定動畫時間戳
                                            clipState.time += deltaTime;
                                        }
                                        // 停止播放動畫
                                        m_animation.Stop();
                                    }
                                
                                }
                                

                                需要提醒的是,這個方案是利用空間換時間,如果模型頂點資料特別多或動畫時長特別長的時候,這時就會遇到記憶體瓶頸。

                                五、純程式碼動態建立網格

                                一般情況下,網格是事先製作好的資源,但也有一些特殊的需求需要在程式碼中動態建立網格。
                                比如我之前寫的一篇牙齒碎了的文章:《Unity 2D圖片任意形狀破碎碎裂效果,以此紀念我的牙光榮犧牲》

                                遊戲開發進階Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)

                                現在我來教大家如何使用程式碼從零建立網格並將網格渲染出來,下文我以建立一個正方形網格為例進行講解。

                                1、建立Mesh物件

                                第一步最簡單,就是直接new一個Mesh

                                var mesh = new Mesh();
                                

                                2、頂點座標

                                首先分析一下,一個四邊形有四個頂點,假設正方形邊長為1,四個點的座標如下,

                                在這裡插入圖片描述

                                寫成程式碼就是這樣:

                                // 構建頂點座標
                                var vertices = new List<Vector3>();http://www.cppcns.com
                                vertices.Add(new Vector3(-0.5f,-0.5f,0));
                                vertices.Add(new Vector3(-0.5f,0.5f,0));
                                vertices.Add(new Vector3(0.5f,0));
                                // 將頂點座標設定給Mesh
                                mesh.SetVertices(vertices);
                                

                                3、UV座標

                                UV座標就是紋理貼圖座標,它將紋理上每一個點精確對應到模型物體的表面上,注意UV的取值範圍是0~1
                                UV座標系原點在左下角,U軸是水平軸,V軸是豎直軸,如下:

                                在這裡插入圖片描述

                                對應到我們的上面那個正方向網格的話,四個點的UV座標如下:

                                在這裡插入圖片描述

                                寫成程式碼就是這樣:

                                // 構建UV座標
                                var uvs = new List<Vector2>();
                                uvs.Add(new Vector2(0,0));
                                uvs.Add(new Vector2(www.cppcns.com0,1));
                                uvs.Add(new Vector2(1,0));
                                // 將UV座標設定給Mesh
                                mesh.SetUVs(0,uvs);
                                

                                4、三角形序列

                                網格需要切分成三角形,我們可以這樣切分,

                                在這裡插入圖片描述

                                當然也可以這樣切分,

                                在這裡插入圖片描述

                                兩種切分方法對應不同的三角形序列,假設 法線方向 是垂直於螢幕從內指向螢幕外的話,第一種切分方式的三角形序列如下:

                                注:法線的方向就決定了表面正面,如果你的材質是單面渲染的話,那麼只有從正面看才能看到網格被渲染。

                                在這裡插入圖片描述

                                即三角形序列為:{ 0,1,2,3 },注意序號是從0開始的。
                                為什麼是這樣的順序呢?我教大家一個技巧,伸出你的左手,豎起大拇指,像這樣子,

                                在這裡插入圖片描述

                                大拇指指向法線的方向,那麼此時你的其餘四根手指頭環繞的方向就是三角形的序號的順序,三個序號為一組按順序塞入陣列中即可,即得到的陣列就是:{ 0,3}當然,以下陣列最終的效果都是等價的,只要順序一致即可:

                                { 0,3 },
                                { 1,3 www.cppcns.com},
                                { 0,3,0 },

                                我們現在寫成程式碼,

                                // 重新計演算法線,注意,法線是根據共享的頂點計算出來的。
                                mesh.RecalculateNormals();
                                
                                // 重新計算包圍體,在修改頂點後需要這個函式以確保包圍體是正確的
                                mesh.RecalculateBounds();
                                
                                // 從法線和紋理座標重新計算網格的切線(如果網格使用引用法線貼圖的著色器進行渲染,則切線需要更新)
                                // 因為我們這裡不使用法線貼圖,所以就不呼叫它了
                                // mesh.RecalculateTangents();
                                

                                5、重新計演算法線和包圍體

                                當我們設定或修改了頂點資料後,需要呼叫MeshRecalculate方法來重新計算一些必要的資訊,比如重新計演算法線、包圍體,程式碼如下

                                // 重新計演算法線,注意,法線是根據共享的頂點計算出來的。
                                mesh.RecalculateNormals();
                                
                                // 重新計算包圍體,在修改頂點後需要這個函式以確保包圍體是正確的
                                mesh.RecalculateBounds();
                                
                                // 從法線和紋理座標重新計算網格的切線(如果網格使用引用法線貼圖的著色器進行渲染,則切線需要更新)
                                // 因為我們這裡不使用法線貼圖,所以就不呼叫它了
                                // mesh.RecalculateTangents();
                                

                                6、完整版程式碼

                                以上程式碼封裝成GenQuadMesh.cs指令碼,完整程式碼如下:

                                // 使用程式碼生成四邊形網格
                                using System.Collections.Generic;
                                using UnityEngine;
                                [RequireComponent(typeof(MeshFilter))]
                                [RequireComponent(typeof(MeshRenderer))]
                                public class GenQuadMesh : MonoBehaviour
                                {
                                    public MeshFilter mf;
                                    private void Start()
                                    {
                                        mf.mesh = Build();
                                    }
                                   public static Mesh Build()
                                    {
                                        var mesh = new Mesh();
                                        // 構建頂點座標
                                        var vertices = new List<Vector3>();
                                        vertices.Add(new Vector3(-0.5f,0));
                                        vertices.Add(new Vector3(-0.5f,0));
                                        vertices.Add(new Vector3(0.5f,0));
                                        // 將頂點座標設定給Mesh
                                        mesh.SetVertices(vertices);
                                        // 構建UV座標
                                        var uvs = new List<Vector2>();
                                        uvs.Add(new Vector2(0,0));
                                        uvs.Add(new Vector2(0,1));
                                        uvs.Add(new Vector2(1,0));
                                        // 將UV座標設定給Mesh
                                        mesh.SetUVs(0,uvs);
                                        // 設定三角形序列
                                        var triangles = new int[] { 0,3 };
                                        mesh.SetTriangles(triangles,0);
                                        mesh.RecalculateNormals();
                                        mesh.RecalculateBounds();
                                        return mesh;
                                    }
                                }
                                

                                7、測試

                                建立一個空物體,掛上MeshFilterMeshRenderer元件。

                                在這裡插入圖片描述

                                再掛上我們上面寫的GenQuadMesh指令碼,賦值mf變數為MeshFilter物件,如下

                                在這裡插入圖片描述

                                執行Unity,看到一個紫色快,

                                在這裡插入圖片描述

                                Scene檢視的模式設定為Wireframe,如下

                                在這裡插入圖片描述

                                現在我們可以看到我們動態建立的網格啦,

                                在這裡插入圖片描述

                                上面之所以顯示紫色塊,是因為我們沒有給MeshFilter設定材質球,順手做一個炮姐的材質球吧,

                                在這裡插入圖片描述

                                MeshRenderer設定材質球物件,

                                在這裡插入圖片描述

                                重新執行Unity,效果如下,

                                在這裡插入圖片描述

                                8、專案原始碼

                                要用程式碼動態建立一個Mesh,就是new一個Mesh,給它塞入頂點座標、UV座標和三角形序列即可。再複雜的網格也可以通過這些步驟創建出來~
                                下面這些就是使用純程式碼創建出來的幾何體網格,感興趣的同學可以下載專案原始碼下來學習。
                                專案原始碼:https://codechina.csdn.net/linxinfa/unity-mesh-builder

                                在這裡插入圖片描述

                                六、網格相關的開源專案

                                我再推薦一些網格相關的開源專案給大家~

                                1、2D網格塗鴉

                                專案地址:https://.com/mattatz/unity-triangulation2D

                                在這裡插入圖片描述

                                2、3D網格塗鴉

                                專案地址:https://github.com/mattatz/unity-teddy

                                在這裡插入圖片描述

                                3、網格體素化

                                專案地址:https://github.com/Scrawk/Mesh-Voxelization

                                在這裡插入圖片描述

                                在這裡插入圖片描述

                                4、網格平滑演算法

                                專案地址:https://github.com/mattatz/unity-mesh-smoothing

                                在這裡插入圖片描述

                                5、網格切割

                                專案地址:https://github.com/hugoscurti/mesh-cutter

                                在這裡插入圖片描述

                                在這裡插入圖片描述

                                6、網格合併

                                專案地址:https://github.com/sanukin39/UniMeshCombiner

                                在這裡插入圖片描述

                                七、未完的探險

                                好了,這次探險之旅就暫時到這裡吧,還有很多內容需要探索,先保持體力,我們下次再見,更多關於Unity網格(Mesh\動態合批\骨骼動畫\蒙皮)的資料請關注我們其它相關文章!