1. 程式人生 > >Unity流水賬8:Mesh

Unity流水賬8:Mesh

Mesh
允許從指令碼建立或修改網格的類。
Meshes包含定點和多個三角形陣列。
三角形陣列只是頂點陣列的索引,每個三角形有三個索引。
對於每個定點,可以有一個法線,兩個紋理座標,顏色和切線。但這些是可選的,可以隨意刪除。所有的頂點資訊都儲存在大小相同的單獨陣列中,所以如果網格有10個頂點,那麼對於法線和其他屬性也有10個大小的陣列。
1.從頭開始構建網格,應該按照以下順序進行:

a)指定頂點
b)指定三角形
using UnityEngine;
public class ExampleClass:MonoBehaviour
{
	public Vector3[] newVertices;
	public Vector2[] newUV;
	public int[] newTriangles;
	void Start()
	{
		Mesh mesh = new Mesh();
		GetComponent<MeshFilter>().mesh = mesh;
		mesh.vertices = newVertices;
		mesh.uv = newUV;
		mesh.triangles = newTriangles;
	}
}

2.每幀修改頂點屬性

a)獲取頂點
b)修改它們
c)將它們分配會mesh
using UnityEngine;
public class ExampleClass:MonoBehaviour
{
	void Update()
	{
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		Vector3[] vertices = mesh.vertices;
		Vector3[] normals = mesh.normals;
		int i = 0;
		while(i < vertices.Length)
		{
			vertices[i] += normals[i] * Mathf.Sin(Time.time);
			i++;
		}
		mesh.vertices = vertices;
	}
}

3.連續改變網格的三角形和頂點

a)呼叫clear重新開始
b)分配頂點和其他屬性
c)分配三角形索引

在分配新的定點或三角形之前呼叫Clear是很重要的。Unity總是檢查提供的三角形索引是否沒有引用超出邊界的頂點。呼叫Clear,然後分配頂點,確保三角形索引中不會有超出邊界的資料。

using UnityEngine;
public class ExampleClass:MonoBehaviour
{
	Vector3[] newVertices;
	Vector2[] newUV;
	int[] newTriangles;
	void Start()
	{
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		mesh.Clear();
		//Do some calculations...
		mesh.vertices = newVertices;
		mesh.uv = newUV;
		mesh.triangles = newTriangles;
	}
}

Properties

bindposes:每個索引處的繫結姿勢指的是索引相同的bone(見參考資料2)

bindpose定義

程式碼實現

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Animation animation = gameObject.AddComponent<Animation>();
        SkinnedMeshRenderer render = gameObject.AddComponent<SkinnedMeshRenderer>();
        Mesh mesh = new Mesh();
        //在mesh區域性空間中的座標
        mesh.vertices = new Vector3[] { new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(-1, 5, 0), new Vector3(1, 5, 0) };
        mesh.uv = new Vector2[] { new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1) };
        mesh.triangles = new int[]{ 0, 1, 2, 1, 3, 2 };
        mesh.RecalculateNormals();
        render.material = new Material(Shader.Find("Diffuse"));
        BoneWeight[] weights = new BoneWeight[4];
        //頂點0受骨骼0控制
        weights[0].boneIndex0 = 0;
        weights[0].weight0 = 1;
        //頂點1受骨骼0控制
        weights[1].boneIndex0 = 0;
        weights[1].weight0 = 1;
        //頂點2受骨骼1控制
        weights[2].boneIndex0 = 1;
        weights[2].weight0 = 1;
        //頂點1受骨骼1控制
        weights[3].boneIndex0 = 1;
        weights[3].weight0 = 1;
        mesh.boneWeights = weights;
        Transform[] bones = new Transform[2];
        Matrix4x4[] bindPoses = new Matrix4x4[2];
        //骨骼0
        bones[0] = new GameObject("Lower").transform;
        bones[0].parent = transform;
        bones[0].localRotation = Quaternion.identity;
        bones[0].localPosition = Vector3.zero;
        bindPoses[0] = bones[0].worldToLocalMatrix * transform.localToWorldMatrix;
        //骨骼1
        bones[1] = new GameObject("Upper").transform;
        bones[1].parent = transform;
        bones[1].localRotation = Quaternion.identity;
        bones[1].localPosition = new Vector3(0, 5, 0);
        bindPoses[1] = bones[1].worldToLocalMatrix * transform.localToWorldMatrix;
        //bindposes:在繫結時刻將頂點由mesh的區域性空間座標變換到bone的區域性空間的變換
        //個人理解:當改變bone的位置時,根據bindpose的值將頂點從mesh(此時是當前指令碼所掛物體)的區域性空間中變換到世界空間中
        //然後變換到原始骨骼的區域性空間,然後乘以當前骨骼從區域性空間變換到世界空間的矩陣,將頂點變換到世界空間。
        mesh.bindposes = bindPoses;
        render.bones = bones;
        render.sharedMesh = mesh;
        AnimationCurve curve = new AnimationCurve();
        curve.keys = new Keyframe[] { new Keyframe(0, 0, 0, 0), new Keyframe(1, 3, 0, 0), new Keyframe(2, 0, 0, 0) };
        AnimationClip clip = new AnimationClip();
        //改變骨骼0的位置,頂點0和頂點1受影響
        clip.SetCurve("Lower", typeof(Transform), "m_LocalPosition.z", curve);
        clip.legacy = true;
        animation.AddClip(clip, "test");
        animation.Play("test");
    }
}
blendShapeCount:返回這個mesh的blendshape個數
boneWeight:每個頂點對應的骨骼權重
bounds:mesh的包圍盒

程式碼實現

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Vector2[] uvs = new Vector2[vertices.Length];
        Bounds bound = mesh.bounds;
        int i = 0;
        while(i<uvs.Length)
        {
            uvs[i] = new Vector2(vertices[i].x / bound.size.x, vertices[i].z / bound.size.x);
            i++;
        }
        mesh.uv = uvs;
    }
}
colors:mesh的頂點顏色
colors32:mesh的頂點顏色,效能比colors好,避免了位元組到浮點的顏色轉換,並且使用更少的臨時記憶體

程式碼實現
UI設定
顯示效果

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Color[] colors = new Color[vertices.Length];
        for (int i = 0; i < vertices.Length; i++)
            colors[i] = Color.Lerp(Color.red, Color.green, vertices[i].y);
        mesh.colors = colors;
    }
}
indexFormat:mesh索引緩衝區資料的格式。索引緩衝區可以是16位(在mesh中支援最多65535(2^16-1)個頂點)。也可以是32位(支援最多40億個頂點)。預設的索引是16位,因為這樣佔用的記憶體和頻寬更少。注意,GPU對32位索引的支援並不是在所有平臺都得到保證的。例如,搭載mali400 GPU的安卓裝置不支援。當在這樣的平臺使用32位索引時,將記錄一條警告訊息,且mesh不會被渲染。
isReadable:返回一個bool值指示mesh是否啟用read/write。這個值可以在匯入模型的時候使用Read/Write Enabled複選框設定,或者呼叫Mesh.UploadMeshData時設定markNoLongerReadable值。從指令碼建立的動態mesh總是返回true.沒有開啟Read/Write的Mesh將在執行時從指令碼訪問任何資料陣列時丟擲錯誤。在遊戲和渲染迴圈之外的Unity編輯器中允許訪問。

程式碼實現

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        //輸出true
        print(mesh.isReadable);
        mesh.UploadMeshData(false);
        //輸出true
        print(mesh.isReadable);
        mesh.UploadMeshData(true);
        //輸出false
        print(mesh.isReadable);
    }
}
normals:mesh的法線

程式碼實現

using UnityEngine;
public class MeshTest : MonoBehaviour {
    float speed = 100.0f;
    Mesh mesh = null;
    private void Update()
    {
        mesh = GetComponent<MeshFilter>().mesh;
        //如果沒有勾上Read/Write複選框不起作用
        mesh.UploadMeshData(false);
        Vector3[] normals = mesh.normals;
        Quaternion rotation = Quaternion.AngleAxis(Time.deltaTime * speed, Vector3.up);
        for (int i = 0; i < normals.Length; i++)
        {
            normals[i] = rotation * normals[i];
            Debug.DrawLine(mesh.vertices[i], normals[i]);
        }
        mesh.normals = normals;
    }
}
subMeshCount:Mesh物件內的子mesh數量,每個子mesh對應於Renderer中的一個材質,例如MeshRenderer或SkinnedMeshRenderer.子mesh由一系列三角形組成,這些三角形由一組頂點組成。頂點可以在多個子mesh之間共享。參見:GetTriangles,SetTriangles.

程式碼實現:

using UnityEngine;
public class MeshTest:MonoBehaviour{
	private void Start()
	{
			Mesh mesh = GetComponent<SkinnedMeshRender>().sharedMesh;
			Debug.Log("Submenshes :  " + mesh.subMeshCount);
	}
}
tangents:網格的切線。切線大部分用於凹凸貼圖的shader.切線是一個單位長度的向量,沿著網格表面沿水平(U)紋理方向運動。Unity中的切線表示為Vector4,其中x,y,z分量定義了這個向量,w用於翻轉副法線(如果需要)。

定義補充:
1.三角形法線:一個平面的法線是一個長度為1並且垂直於這個平面的向量。一個三角形的法線是一個長度為1並且垂直於這個三角形的向量。通過簡單地將三角形兩條邊進行叉乘計算(向量a和b的叉乘結果是一個同時垂直於a和b的向量),然後歸一化:使長度為1.
2.頂點法線:包含該頂點的所有三角形的法線的均值。這帶來了不少便利-因為在頂點著色器中,我們處理頂點,而不是三角形;所以最好把資訊放在頂點上。況且在OpenGl中,我們沒有任何辦法獲取三角形資訊。
3.切線空間:Up向量即法線(可以用Blender生成)或由一個簡單的叉乘計算得到。切線T:垂直於法線的向量(為了保持連續一致性,以免銜接處出現瑕疵。標準做法是將切線方向和紋理空間對齊)。副切線B:(本可以隨便選一個切線,但是選定垂直於另外兩條軸的切線,計算會方便一些)。

演算法如下:記三角形的兩條邊為deltaPos1和deltaPos2,deltaUV1和deltaUV2是對應的UV座標下的差值。則問題可用如下方程表示:
deltaPos1 = deltaUV1.x * T + deltaUV1.y * B;
deltaPos2 = deltaUV2.x * T + deltaUV2.y * B;
解方程即可的切線和副切線。
triangles:包含Mesh中所有的三角形陣列。陣列是包含定點陣列的索引的三角形列表。三角形陣列的大小必須是3的倍數。可以通過簡單地索引到同一個頂點來共享頂點。如果網格包含多個子網格(Materials),則三角形列表將包含屬於其所有子網格的所有三角形。當你使用這個函式分配一個三角形陣列,subMeshCount被設定為1。如果你想有多個子網格,使用subMeshCount以及SetTriangles.在分配頂點陣列之後,建議分配一個三角形陣列,以避免超出邊界的錯誤。

程式碼實現:
程式碼1

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        gameObject.AddComponent<MeshFilter>();
        gameObject.AddComponent<MeshRenderer>();
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        mesh.Clear();
        //左手定則:順時針,法線朝螢幕外面(注意攝像機位置),game檢視可以看到
        mesh.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(1, 1, 0), new Vector3(1, 0, 0) };
        //左手定則:逆時針,法線朝螢幕裡面(注意攝像機位置),game檢視看不到
        //mesh.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 1, 0) };
        mesh.uv = new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1) };
        mesh.triangles = new int[] { 0, 1, 2 };
    }
}
uv:Mesh的基本紋理座標。注意:紋理座標必須從Mesh外部建立或修改。下面的指令碼示例建立uvs陣列,為uvs陣列分配紋理座標,然後將陣列分配給網格。
uv2:Mesh的第二個紋理座標集,如果存在
uv3:Mesh的第三個紋理座標集,如果存在
uv4:Mesh的第四個紋理座標集,如果存在
uv5:Mesh的第五個紋理座標集,如果存在
uv6:Mesh的第六個紋理座標集,如果存在
uv7:Mesh的第七個紋理座標集,如果存在
uv8:Mesh的第八個紋理座標集,如果存在

程式碼實現:

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Vector2[] uvs = new Vector2[vertices.Length];
        for(int i = 0; i < uvs.Length; i++)
        {
            uvs[i] = new Vector2(vertices[i].x, vertices[i].z);
        }
        mesh.uv = uvs;
        //mesh.uv2 = uvs;
        //mesh.uv3 = uvs;
        //mesh.uv4 = uvs;
        //mesh.uv5 = uvs;
        //mesh.uv6 = uvs;
        //mesh.uv7 = uvs;
        //mesh.uv8 = uvs;
    }
}
vertexBufferCount:獲取Mesh中存在的頂點緩衝區的數量(只讀)。大多數網格只包含一個頂點緩衝區,但有些(例如某些平臺上的skinned Meshes)可能包含多個頂點緩衝區。這個屬性通常與GetNativeVertexBufferPtr一起使用,以支援原生代碼外掛的網格操作。
vertexCount:返回Mesh中頂點的數量(只讀)

程式碼實現:

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        print(mesh.vertexCount);
    }
}
vertices:返回頂點位置的拷貝或分配一個新的頂點位置的陣列。網格中的頂點數通過分配一個頂點陣列來改變。注意,如果調整頂點陣列的大小,那麼所有其他頂點屬性(法線,顏色,切線,uv)也會自動調整大小。如果在設定頂點時沒有給Mesh分配頂點,則自動呼叫RecalculateBounds.
注意:要更改頂點,從Mesh中複製頂點時很重要的。一旦頂點被複制和改變,頂點可以重新分配回網格。

程式碼實現:

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Update()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        int i = 0;
        while(i < vertices.Length)
        {
            vertices[i] += Vector3.up * Time.deltaTime;
            i++;
        }
        mesh.vertices = vertices;
        mesh.RecalculateBounds();
    }
}

Constructors

Mesh:建立一個空的Mesh

程式碼示例

using UnityEngine;
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;
    }
}

Public Methods

AddBlendShapeFrame:新增一個新的blend shape frame。如果blend shape名稱不存在,則建立一個新的blend shape.Blend shape frames只能新增到一個新的blend shape,或最後的blend shape.通常情況下,blend shape只有一個frame,但是混合範圍[0-100%]可能被分割成多個框架。當一個shape只有一個frame時,假定權重為100%.對於具有多個frame的blend shape,必須以增加權重的順序新增frame.deltaVertices,deltaNormals和deltaTangents陣列的大小必須等於Mesh.vertexCount.減去Mesh vertices,normals或者tangents從frame完整的vertors轉換得到deltas.deltaNormals或deltaTangents可以被設定為null,如果一個frame沒有normals或tangents.

函式定義:public void AddBlendShapeFrame(string shapeName,float frameWeight,Vector3[] deltaVertices,Vector3[] deltaNormal,Vector3[] deltaTangents
函式引數:
1.shapeName:要新增的frame的blend shape的名稱
2.frameWeight:正在新增的frame的權重
3.deltaVertices:正在新增的frame的Delta頂點
4.deltaNormals:正在新增的frame的Delta法線
5.deltaTangents:正在新增的frame的Delta切線
Clear:清除所有頂點資料和所有三角形索引。你應該在重建三角形陣列之前呼叫這個函式。

函式定義:public void Clear(bool keepVertexLayout);
程式碼實現:
//該函式的預設行為保持現有頂點佈局:例如,如果Mesh有切向量和頂點顏色,那麼當你填充新的頂點資料時,切線和顏色將成為網格資料的一部分。如果你想完全清除網格並從一個空頂點佈局開始,傳遞false作為keepVertexLayout引數。另外,為任何Mesh元件分配一個空陣列也將從頂點佈局中移除它。
using UnityEngine;
public class MeshTest : MonoBehaviour {
    private bool once = false;
    private void Update()
    {
        if(Time.time > 2.0f)
        {
            convertMesh();
        }
    }
    void convertMesh()
    {
        if (once)
            return;
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        //清除網格當前擁有的所有資料
        mesh.Clear();
        //為三角形建立3個頂點
        mesh.vertices = new Vector3[] { new Vector3(0, 0, 0), new Vector3(0, 1, 0), new Vector3(1, 1, 0) };
        mesh.uv = new Vector2[] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1) };
        mesh.triangles = new int[] { 0, 1, 2 };
        once = true;
    }
}
ClearBlendShapes:從Mesh中清除所有blend shape
CombineMeshes:將幾個Mesh合併到這個Mesh中。組合網格有助於優化效能。如果mergeSubMeshes為真,所有網格都組合到一個sub-mesh中。否則,每個Mesh被放置在不同的sub-mesh中。如果所有mesh共享相同的材質,這個屬性應該設定為true。
如果useMatrices為真,則使用CombineInstance結構體中的轉換矩陣.否則,它們就會被忽略。
設定hasLightmapData為true,通過CombineInstance結構體中的lightmap scale offset陣列轉換輸入網格lightmap uv資料。網格必須共享相同的lightmap紋理。

函式定義:public void CombineMeshes(CombineInstance[] combine,bool mergeSubMeshes = true,bool useMatrices = true,bool hasLightmapData = false);
函式引數:
1.combine:描述要合併的mesh
2.mergeSubMeshes:定義Mesh是否應合併為單個子網格
3.useMatrices:定義CombineInstance陣列中提供的transform應使用還是忽略。
程式碼實現:
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class MeshTest : MonoBehaviour {
    private void Start()
    {
        MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combine = new CombineInstance[meshFilters.Length];
        int i = 0;
        while(i < meshFilters.Length)
        {
            combine[i].mesh = meshFilters[i].sharedMesh;
            combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
            meshFilters[i].gameObject.active = false;
            i++;
        }
        transform.GetComponent<MeshFilter>().mesh = new Mesh();
        transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
        transform.gameObject.active = true;
    }
}
GetBaseVertex:獲取給定sub-mesh的基頂點索引。當使用16位索引緩衝區時,可以使用基頂點來實現大於65535個頂點的網格,只要每個子網格適合於它自己的65535個頂點。另外32位緩衝區可以通過indexFormat使用。
函式定義:public uint GetBaseVertex(int submesh)
函式引數:
1.submesh:sub-mesh索引
函式返回值:uint,應用於這個sub-mesh的所有索引的偏移量。
GetBindposes:獲取此例項的bind poses。每個索引處的bind pose對應於具有相同索引的骨骼。如果你想控制傳遞進來的列表的生命週期,並且希望避免每次訪問時分配新陣列,則使用此方法而不是bindpose.
函式定義:public void GetBindposes(List<Matrix4x4>bindposes)
函式引數:
1.bindposes:要填充的bind poses列表
GetBindShapeFrameCount:返回blend shape的幀數
函式定義:public int GetBlendShapeFrameCount(int shapeIndex)
函式引數:
1.shapeIndex:獲取幀數的shape索引。
GetBlendShapeFrameVertices:返回blend shape frame的deltaVertices,deltaNormals以及deltaTangents。deltaVetrices,deltaNormals以及deltaTangents陣列的大小必須等於Mesh.vertexCount。新增Mesh vertices,normals或者tangents從frame deltas轉換為完整的vectors.deltaNormals或deltaTangents可以被設定為null,如果frame中沒有法線或者切線。
函式定義:public void GetBlendShapeFrameVertices(int shapeIndex,int frameIndex,Vector3[] deltaVertices,Vector3[]deltaNormals,Vector3[]deltaTangents);
函式引數:
1.shapeIndex:frame的shape索引
2.frameIndex:從frame索引中獲取權重
3.deltaVertices:得到frame的delta頂點輸出陣列
4.deltaNormals:得到frame的delta法線輸出陣列
5.deltaTangents:得到frame的delta切線輸出陣列
GetBlendShapeFrameWeight:返回blend shape frame的權重
函式定義:public float GetBlendShapeFrameWeight(int shapeIndex,int frameIndex)
函式引數:
1.shapeIndex:frane的shape索引
2.frameIndex:獲取權重的frame索引
GetBlendShapeIndex:按給定名稱返回Blend Shape的索引,如果不存在名為blendShapeName的BlendShape,返回-1.
函式定義:public int GetBlendShapeIndex(string blendShapeName)
GetBlendShapeName:根據給定的索引返回BlendShape的名稱
函式定義:public string GetBlendShapeName(int shapeIndex) 
GetBoneWeights:獲取此例項的骨骼重量。如果這個例項定義的骨骼權重,那麼每個索引處的骨骼權重對應於具有相同索引的頂點。否則列表將為空。如果你想控制傳遞進來的列表的生命週期,並且希望避免在每次訪問時分配新陣列,則使用此方法而不是boneWeights.
函式定義:public void GetBoneWeights(List<BoneWeight>boneWeights);
函式引數:
boneWeights:要填充的骨骼權重列表
GetColors:獲取此例項的頂點顏色。每個索引處的顏色對應於具有相同索引的頂點。如果你想控制傳入的列表的生命週期,並且希望避免在每次訪問時分配一個新陣列,那麼請使用此方法,而不是color或color32.
函式定義:
public void GetColors(List<Color> colors)
public void GetColors(List<Color32>colors)
函式引數:
colors:要填充的頂點顏色列表
GetIndexCount:獲取給定sub-mesh的索引計數
函式定義:public uint GetIndexCount(int submesh)
GetIndexStart:獲取給定sub-mesh中mesh索引緩衝區內的起始索引位置。
函式定義:public uint GetIndexStart(int submesh);
GetIndices:獲取指定子網格的索引列表。返回列表中的每個整數都是一個頂點索引,它用作Mesh頂點陣列的偏移量。索引的佈局取決於sub-mesh的MeshTopology。例如三角形mesh將以3的倍數返回索引。
sub-mesh表示使用單一材質呈現的三角形列表(或具有不同MeshTopology結構的索引)。當Mesh與具有多個材質的Renderer一起使用時,你應該確保每個material都有一個sub-mesh.
如果你控制緩衝區的生命週期,並且希望避免在每次訪問時分配新陣列,那麼可以使用List<int>引數呼叫方法過載。
函式定義:
public void GetIndices(List<int>indices,int submesh);
public int[] GetIndices(int submesh);
函式引數:
1.indices:要填充的索引列表
2.submesh:sub-mesh的索引
3.applyBaseVertex:True(預設值)將返回的索引應用基頂點偏移量。
GetNativeIndexBufferPtr:檢索指向索引緩衝區的本地指標(底層圖形API)。使用此函式檢索與Mesh索引緩衝區對應的指標/控制代碼,作為它在本地圖形API中的表示。這可以用於從原生代碼外掛中啟用Mesh操作。
根據indexFormat,索引緩衝區資料在每個索引中為16位或32位。索引緩衝區的佈局取決於MeshTopology.最常見的情況是由三角形列表組成的Mesh,每個三角形有三個索引緩衝區。
返回的資料型別取決於底層圖形API:
--IDirect3DIndexBuffer9 on D3D9
--ID3D11Buffer on D3D11
--ID3D12Resource on D3D12
--buffer "name" (as GLuint) on OpenGL/ES
--id<MTLBuffer> on Metal
對於大多數用例(當從原生代碼編寫Mesh資料時),在獲得本地緩衝指標之前,你需要將網格標記為“dynamic”。通常這將緩衝區切換為CPU-writable。
注意。在使用多執行緒渲染時呼叫此函式將與渲染執行緒同步(這是一個緩慢的操作),因此最佳實踐是僅在初始化時設定必要的緩衝區指標。

函式定義:public IntPtr GetNativeIndexBufferPtr()
函式返回值:指向底層圖形API索引緩衝區的IntPtr指標。
GetNativeVertexBufferPtr:檢索一個指向頂點緩衝區的本地(底層圖形API)指標。使用此函式檢索與mesh頂點緩衝區對應的指標/控制代碼,作為它在本機圖形API中的表示。這可以用於從原生代碼外掛中啟用mesh操作。
大多數網格只包含一個頂點緩衝區,但有些(例如某些平臺上的skinned Meshes)可能包含多個頂點緩衝區。使用vertexBufferCount查詢頂點緩衝區計數。頂點緩衝區的資料佈局通常取決於許多因素,特別是對於被壓縮的Mesh(參見PlayerSetting->Mesh Compression Settings)和標記為non-readable。對於一個簡單的例子,佈局大致如下
float3 position(12bytes)
float3 normal(12bytes)
byte4 color32(4bytes) 或者 float4 color(16 bytes)
float2|float3|float4 uv(8,12或者16bytes)
float2|float3|float4 uv2(8,12或者16bytes)
float2|float3|float4 uv3(8,12或者16bytes)
float2|float3|float4 uv4(8,12或者16bytes)
float4 tangent(16bytes)
所有頂點元件都是可選的,例如一個mesh可能只包含position+normal+一個二維紋理座標。在這種情況下,緩衝區中的頂點資料大小為12+12+8=32bytes.
返回的資料型別取決於底層圖形API:
--IDirect3DIndexBuffer9 on D3D9
--ID3D11Buffer on D3D11
--ID3D12Resource on D3D12
--buffer "name" (as GLuint) on OpenGL/ES
-- id<MTLBuffer> on Metal
對於大多數用例(i.e.從原生代碼中編寫Mesh資料),在獲得本機緩衝區指標之前,你需要將網格標記為"dynamic"(請參閱MarkDynamic)。通常這將緩衝區切換為CPU-writable.
注意。在使用多執行緒渲染時呼叫此函式將與渲染執行緒同步(這是一個緩慢的操作),因此最佳實踐是僅在初始化時設定必要的緩衝區指標。

函式定義:public IntPtr GetNativeVertexBufferPtr(int index)
函式引數:
1.bufferIndex:要獲取哪個頂點緩衝區(有些網格可能不止一個)。
函式返回值:IntPtr指向底層圖形API頂點緩衝區的指標。
GetNormals:獲取此例項的頂點法線。每個索引處的法線對應於具有相同索引的頂點。如果你想控制傳入的列表的生命週期,並且希望避免在每次訪問時分配一個新陣列,那麼請使用此方法而不是normals方法。
函式定義:public void GetNormals(List<Vector3>normals);
函式引數:
1.normals:要填充的頂點法線列表
GetTangents:獲取此例項的切線。每個索引處的切線對應於具有相同索引的頂點。如果你想控制傳入的列表的生命週期,並且希望避免在每次訪問時分配一個新陣列,那麼請使用此方法而不是normals方法。
函式定義:public void GetTangents(List<Vector4>tangents);
函式引數:
1.tangents:要填充的切線的列表
GetTopology:獲取sub-mesh的topology
函式定義:public MeshTopology GetTopology(int submesh)
GetTriangles:獲取此物件上指定sub-mesh的三角形列表。返回的三角形列表中的每個整數都是一個頂點索引,它用作Mesh的頂點陣列偏移量。三角形列表包含三個索引的倍數,每個索引對應一個三角形中的每個角。sub-mesh表示使用單一材質渲染的三角形列表。當Mesh與具有多個材質的渲染器一起使用時,你應該確保每個材質都有一個sub-mesh。如果你控制索引緩衝區的生命週期,並且希望避免在每次訪問時分配新陣列,那麼可以使用List<int>引數呼叫此方法過載。
函式定義:
public int[] GetTriangles(int submesh);
public int[] GetTriangles(int submesh,bool applyBaseVertex = true);
public void GetTriangles(List<int>triangles,int submesh,bool applyBaseVertex = true)
public void GetTriangles(List<int>triangles,int submesh);
函式引數:
1.triangles:要填充的頂點索引表。
2.submesh:sub-mesh索引
3.applyBaseVertex:True(預設值)將對返回的索引應用base vertex offset.
GetUVDistributionMetric:UV分佈度可用於根據攝像機位置計算所需的mipmap level.
函式定義:public float GetUVDistributionMetric(int uvSetIndex)
函式引數:
1.uvSetIndex:UV設定索引返回UV分佈度,第一次為.0
函式返回值:三角形面積/UV面積的float平均值。
程式碼示例:
示例程式碼展示瞭如何將該值與一些攝像機屬性一起使用,以計算所需的mipmap級別。
using UnityEngine;
public class MeshTest : MonoBehaviour {
    private Vector3 m_CameraPosition;
    private float m_CameraEyeToScreenDistanceSquared;
    private float m_MeshUVDistributionMetric;
    private float m_TexelCount;
    private Texture2D m_Texture;
    public void SetView(Vector3 cameraPostion,float cameraHalfAngle,float screenHalfHeight)
    {
        m_CameraPosition = cameraPostion;
        //得到螢幕距離攝像機的畫素距離
        m_CameraEyeToScreenDistanceSquared = Mathf.Pow(screenHalfHeight / Mathf.Tan(cameraHalfAngle), 2.0f);
    }
    public void SetView(Camera camera)
    {
        float cameraHA = Mathf.Deg2Rad * camera.fieldOfView * 0.5f;
        float screenHH = (float)camera.pixelHeight * 0.5f;
        SetView(camera.transform.position, cameraHA, screenHH);
    }
    private int CalculateMipmapLevel(Bounds bounds,float uvDistributionMetric,float texelCount)
    {
        //基於http://web.cse.ohio-state.edu/~crawfis.3/cse781/Readings/MipMapLevels-Blog.html
        //screenArea = worldArea * (ScreenPixels/(D * tan(FOV)))^2
        //mip = 0.5 * log2(uvArea / screenArea)
        //得到當前模型距離攝像機的距離
        float dSq = bounds.SqrDistance(m_CameraPosition);
        if (dSq < 1e-06)
            return 0;
        //uvDistributionMetric是三角形面積/uv面積(世界空間三角形面積與歸一化uv面積之比)
        //-三角形區域在世界空間中
        //-uv面積是歸一化單位(0->1而不是0->紋理大小)
        //以下個人理解
        //根據模型最大mipmap所佔畫素值/模型當前位置所佔螢幕畫素值 =  螢幕與攝像機畫素距離的平方/物體與攝像機距離的平方,可以求得當前模型所佔畫素(即上文中的screenArea)
        //此時v =  screenArea/uvDistributionMetric.
        float v = (texelCount * dSq) / (uvDistributionMetric * m_CameraEyeToScreenDistanceSquared);
        float desiredMipLevel = 0.5f * Mathf.Log(v, 2);
        return (int)desiredMipLevel;
    }
    public void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        m_Texture = (Texture2D)(GetComponent<MeshRenderer>().sharedMaterial.mainTexture);
        m_MeshUVDistributionMetric = mesh.GetUVDistributionMetric(0);
        //如果mesh 有一個transform scale或者uvscale,它需要在這裡應用。
        Vector3 scale = transform.lossyScale;
        float vertexScale = scale.x * scale.y * scale.z;
        m_MeshUVDistributionMetric *= vertexScale;
        m_TexelCount = m_Texture.width * m_Texture.height;
    }
    public void Update()
    {
        SetView(Camera.main);
        m_Texture.requestedMipmapLevel = CalculateMipmapLevel(GetComponent<Renderer>().bounds, m_MeshUVDistributionMetric, m_TexelCount);
    }
}
GetUVs:獲取mesh的uvs.
函式定義:
1.public void GetUVs(int channel,List<Vector2>uvs);
2.public void GetUVs(int channel,List<Vector2>uvs);
3.public void GetUVs(int channel,List<Vector4>uvs);
函式引數
1.channel:UV通道,索引從0開始對應於UV,注意1對應於UV2.
2.uvs:要填充的UV列表.用Vector2,Vector3或Vector4的列表填充UV列表。
GetVertices:獲取這個例項的頂點座標。如果你要控制傳入的列表的生命週期,並且希望避免在每次訪問時分配一個新陣列,則使用此方法而不是頂點。
函式定義:public void GetVertices(List<Vector3>vertices);
函式引數:
1.vertices:要填充的頂點位置列表
MarkDynamic:優化mesh頻繁更新.在分配頂點之前呼叫這個函式,以在不斷更新Mesh時獲得更好的效能。在內部,這使得mesh在底層API中使用"dynamic buffers",當網格資料經常變換時,這將很有效。
函式定義:public void MarkDynamic()
RecalculateBounds:通過頂點重新計算Mesh的bound。修改頂點後,你應該呼叫此函式以確保bound是正確的。分配三角形會自動重新計算bound的值。
函式定義:public void RecalculateBounds();
程式碼示例:
using UnityEngine;
public class MeshTest : MonoBehaviour
{
    private void Start()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        mesh.RecalculateBounds();
    }
}
RecalculateNormals:通過三角形和頂點重新計算Mesh的法線.。在修改頂點後,通常需要更新法線來反應變化。法線是從所有共享頂點計算的。匯入Mesh有時並不共享所有頂點。例如,UV接縫處的一個頂點就被分割成兩個頂點,因此RecalculateNormal函式建立的法線在UV接縫處並不光滑。
注意,RecalcuLateNormals一般不會自動產生法線,因此在呼叫
函式定義:public void RecalculateNormal();
程式碼示例:
using UnityEngine;
public class MeshTest : MonoBehaviour
{
	private void Start()
	{
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		mesh.RecalculateNormals();
	}
}
RecalculateTangents:從法線和紋理座標重新計算Mesh的切線。修改頂點和Mesh的法線後,如果Mesh使用引用法線貼圖的Shander渲染,那麼切線需要更新。用Mesh的頂點,法線和紋理座標計算切線。
函式定義:public void RecalculateTangents();
程式碼示例:
using UnityEngine;
public class MeshTest:MonoBehaviour
{
	private void Start(0
	{
		Mesh mesh = GetComponent<MeshFilter>().mesh;
		mesh.RecalculateTangents();
	}
}
SetColors:Mesh的頂點顏色
函式定義:
public void SetColors(List<Color>inColors);
public void SetColors(List<Color32>inColors);
函式引數:
1.inColors :每個頂點都具備的顏色
SetIndices:設定sub-mesh的索引緩衝區。sub-mesh表示使用單一材質渲染的三角形列表(或具有不同MeshTopology的indices)。當Mesh與具有多個材質的Renderer一起使用時,你應該確保每個Material都有一個sub-mesh。SetTriangles和Triangles總是設定Mesh由三角形面組成。使用SetIndices建立由線或點組成的Mesh.baseVertex引數可用於在使用16位索引緩衝區的同時實現大於65535個頂點的Mesh,只要每個sub-mesh適合於它自己的65535頂點區域。例如:如果傳遞給SetIndices的索引緩衝區包含了索引10,11,12和baseVertex設定為10000,那麼實際上會使用頂點100010,100011和100012進行渲染。
函式定義:
public void SetIndices(int[] indices,MeshTopology.topology,int submesh,bool calculateBounds);
public void SetIndices(int[] indices,MeshTopology.topology,int submesh);
public void SetIndices(int[] indices,MeshTopology.topology,int submesh,bool calculateBounds = true,int baseVertex = 0);
函式引數:
indices:定義Mesh的索引陣列。
topology:Mesh的topology,例如:三角形,直線,四邊形,點等。
submesh:sub-mesh的子網格修改
calculateBounds:設定indices後計算Mesh的bounding box。預設值為true.當你想要使用現有的bounding box並降低設定indices的CPU成本時,使用false.
baseVertex:新增到所有三角形頂點索引的可選頂點偏移量。
SetNormals:設定Mesh的法線。
函式定義:
public void SetNormals(List<Vertor3>inNormals);
函式引數:
inNormals:每個頂點都具備的法線。
SetTangents:設定Mesh的切線。
函式定義:
public void SetTangents(List<Vector4>inTangents);
函式引數:
inTangents:每個頂點都具備的切線。
SetUVs:設定Mesh的UVs。將Mesh的uvs(紋理座標)設定為一個Vector2、Vector3或Vector4的列表。
函式定義:
public void SetUVs(int channel,List<Vector2>uvs);
public void SetUVs(int channel,List<Vector3>uvs);
public void SetUVs(int channel,List<Vector4>uvs);
函式引數:
1.channel:UV通道。索引從0開始,對應於uv。注意1對應於uv2。
2.uvs:要為給定的索引設定uvs列表。
SetVertices:指定一個新的頂點陣列。
函式定義:
public void SetVertices(List<Vector3>inVertices);
函式引數:
inVertices:每個頂點都具備的位置。
UploadMeshData:上傳之前完成的Mesh修改到圖形API。當通過程式碼(使用vertices,normals,triangles等屬性)建立或修改Mesh時,Mesh資料在內部被標記為“modified(已修改)”,並在下次渲染mesh時被髮送到圖形API。呼叫UploadMeshData立即將修改後的資料傳送到圖形API,以避免稍後可能出現的問題。在markNoLongerReadable引數中傳遞true會使網格資料從指令碼中不再可讀,並釋放了資料的系統記憶體副本。
函式定義:
public void UploadMeshData(bool markNoLongerReadable)
函式引數:
markNoLongerReadable:當設定為true時釋放Mesh資料的系統記憶體副本

繼承的成員
屬性

hideFlags:物件隱藏標誌,儲存在場景中,還是使用者可以修改。
屬性定義:
public HideFlags hideFlags;
name:物件的名字。Component,GameObject和所有附加的Component共享相同的名字。如果一個類派生自MonoBehaviour,那麼它就會從MonoBehaviour繼承"name"欄位。如果這個類也附加到GameObject,那麼"name"欄位將設定為GameObject的名稱。
屬性定義:
public string name;

公共方法

GetInstanceID:返回物件的例項Id.物件的例項ID總是唯一的
函式定義:
public int GetInstanceID();
ToString:返回GameObject的名稱
函式定義:
public string ToString();

靜態方法

Destroy:移除GameObject,Component或Asset。物件obj現在將被銷燬,或者在t秒之後的指定時間被銷燬。如果obj是一個元件,它將從GameObject中移除元件並銷燬它。如果obj是GameObjet(遊戲物件),它將銷燬GameObject(遊戲物體),它的所有Components,以及它的所有子節點。實際的物件銷燬總是延遲到當前更新迴圈之後,但總是在渲染之前完成。
函式定義:
public static void Destroy(Object obj,float t = 0.0F);
函式引數:
1.obj:要移除的物件
2.t:銷燬物件之前要延遲的可選時間量
DestroyImmediate:立即銷燬obj物件。強烈建議使用Destroy代替。
函式定義:
public static void DestroyImmediate(Object obj,bool allowDestroyingAssets = false);
函式引數:
1.obj:被銷燬的物件
2.allowDestroyingAssets:設定為true以允許銷燬asset。這個函式只應該在編寫編輯器程式碼時使用,因為在編輯器模式下永遠不會呼叫延遲銷燬。在遊戲程式碼中,你應該使用Object.Destroy代替。Destroy總是延遲(但是在同一幀中執行)。使用這個函式要小心的是,它會用具的破壞assets。還要注意,永遠不要在遍歷陣列的時候銷燬正在遍歷的元素。
DontDestroyOnLoad:使目標物件在載入新場景時不會被自動銷燬。載入新關卡時,場景中的所有物件都被銷燬,然後載入新關卡中的物件。為了在新場景載入期間儲存一個物件,呼叫DontDestroyOnLoad函式。如果物件是元件或遊戲物件,那麼它的整個hierarchy結構也不會被破壞。
注意:DontDestroyOnLoad沒有返回值。它還打算改變它的引數值。如果需要的話,typeof運算子會進行更改。
函式定義:
public static void DontDestroyOnLoad(Object target)
函式引數:
1.target:在場景變化時不會被銷燬的物件。
FindObjectOfType:返回型別的第一個啟用的載入物件。::ref::.FindObjectOfType不會返回Asset(mesh,textures,prefabs...)或非活動物件。它用於定位GameObject,且不會返回有設定為HideFlags.DontSave的物件。
注意:這個函式非常慢,不建議每一幀都使用這個函式。在大多數情況下,你可以使用單例模式。
函式定義:
public static Object FindObjectOfType(Type type)
函式引數:
type:要查詢的物件型別。
返回值:
Object:返回與指定型別匹配的物件。如果沒有物件匹配該型別,則返回null。
FindObjectsOfType:返回相應型別的所有活動載入物件的列表。它不會返回任何asset(meshes,textures,prefabs,...)或者非啟用物件。不會返回有HideFlags.DontSave設定的物件。使用Resources.FindObjectsOfTypeAll避免這些限制。
函式定義:
public static Object[] FindObjectsOfType(Type type)
函式引數:
type:要查詢的物件型別。
Instantiate:克隆原始物件並返回克隆。此函式以與編輯器中的Duplicate命令相似的方式複製物件。如果你正在克隆一個GameObject,那麼你也可以選擇指定它的position和rotation(這些預設為原始的GameObject的position和rotation).如果你正在克隆一個元件,那麼它所連線到的GameObject也將被克隆,同樣帶有一個可選的positon和rotation。當你克隆一個GameObject或Component時,所有的子物件和元件也將被克隆,其屬性設定與原始物件相同。預設情況下,新物件的父物件為null,所以它不會是原始物件的“sibing(兄弟)”。但是,仍然可以使用過載方法設定父類。如果指定了父物件,而沒有指定position和rotation,則原始物件的position和rotation將用於克隆物件的local postion和roration。如果instantiateInWorldSpace引數為true。如果指定了position和rotation,它們將作為物體在世界空間中的position和rotation.在克隆時,GameObject的啟用狀態將被傳遞,因此,如果原始狀態為非啟用狀態,則克隆出來的物件也將以非啟用狀態建立。在克隆物件之後,你還可以使用GetComponent來設定附加到克隆物件上的特定元件的屬性。
函式定義:
1.public static Object Instantiate(Object original);
2.public static Object Instantiate(Object original,Transform parent);
3.public static Object Instantiate(Object original,Transform parent,bool instantiateInWorldSpace);
4.public static Object Instantiate(Object original,Vector3 position,Quaternion rotation);
5.public static Object Instantiate(Object original,Vector3 position,Quaternion rotation,Transform parent);
6.public static T Instantiate(T original);
7.public static T Instantiate(T original,Transform parent);
8.public static T Instantiate(T original,Transform parent,bool worldPositionStays);
9.public static T Instantiate(T original,Vector3 position,Qaternion rotation);
10.public static T Instantiate(T original, Vector3 position,Quaternion rotation,Transform parent);
函式引數:
1.original:要複製的現有物件
2.position:新物件的position
3.rotation:新物件的orientation
4.parent:將分配給新物件的父節點
5.instantiateInWorldSpace:當分配父物件仍然維持物件的世界位置,而不是設定其相對於新父物件的位置時,傳遞true。傳遞false以設定物件相對於新父物件的位置。
函式返回值:
object:例項化克隆物件

運算子

bool:物件是否存在
程式碼示例
if(rigidbody) 等價於 if(rigidbody == null)
operator != : 比較兩個物件是否引用不同的物件
函式定義
public static bool operator !=(Object x, Object y)
operator == : 比較兩個物件是否引用相同的物件
函式定義:
public static bool operator ==(Object x, Object y)
函式引數:
x:第一個物件
y:跟第一個物件比較的物件