Unity3D學習筆記5——創建子Mesh

1. 概述

在文章Unity3D學習筆記4——創建Mesh高級接口通過高級API的方式創建了一個Mesh,裏面還提到了一個SubMesh的概念。Mesh是對於三維物體對象的封裝概念,一個很容易的需求是,有的地方我希望用到材質A,有的地方我希望用到材質B,我不想把這個Mesh進行拆分,那麼很簡單,就在這個Mesh中劃分兩個子Mesh就可以了。

2. 詳論

2.1. 實現

我們創建如下腳本,並且隨便掛接兩個不同的材質在屬性material1和屬性material2上:

using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class Note5Main : MonoBehaviour
{
    public Material material1;
    public Material material2;
   
    // Start is called before the first frame update
    void Start()
    {
        Mesh mesh = CreateMesh();

        MeshFilter mf = gameObject.GetComponent<MeshFilter>();
        if (mf == null)
        {
            mf = gameObject.AddComponent<MeshFilter>();
        }
        mf.sharedMesh = mesh;

        MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
        if (meshRenderer == null)
        {
            meshRenderer = gameObject.AddComponent<MeshRenderer>();
        }

        Material[] materials = new Material[2];       
        materials[0] = material1;
        materials[1] = material2;
        meshRenderer.materials = materials;
    }

    Mesh CreateMesh()
    {
        Mesh mesh = new Mesh();

        const int vertexCount = 8;
  
        Vector3[] vertices = new Vector3[vertexCount]
        {
            new Vector3(-5, 0, 0),
            new Vector3(-5, 5, 0),
            new Vector3(5, 0, 0),
            new Vector3(5, 5, 0),

            new Vector3(-5, -5, 0),
            new Vector3(-5, 0, 0),
            new Vector3(5, -5, 0),
            new Vector3(5, 0, 0),
        };

        Vector3[] normals = new Vector3[vertexCount]
        {
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),

            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
            new Vector3(0, 0, -1),
        };

        Vector2[] uv = new Vector2[vertexCount]
        {
            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 0),
            new Vector2(1, 1),

            new Vector2(0, 0),
            new Vector2(0, 1),
            new Vector2(1, 0),
            new Vector2(1, 1),
        };

        mesh.vertices = vertices;
        mesh.normals = normals;
        mesh.uv = uv;

        int[] triangles = new int[12] { 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6 };

        MeshUpdateFlags flags = MeshUpdateFlags.DontValidateIndices | MeshUpdateFlags.DontResetBoneBounds
         | MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontRecalculateBounds;
        //MeshUpdateFlags flags = MeshUpdateFlags.Default;

        int indexCount = triangles.Length;
        mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
        mesh.SetIndexBufferData(triangles, 0, 0, indexCount, flags);

        mesh.subMeshCount = 2;
        SubMeshDescriptor subMeshDescriptor1 = new SubMeshDescriptor(0, 6);
        mesh.SetSubMesh(0, subMeshDescriptor1, flags);

        SubMeshDescriptor subMeshDescriptor2 = new SubMeshDescriptor(6, 6);
        mesh.SetSubMesh(1, subMeshDescriptor2, flags);

        return mesh;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

我這裏得到的效果如下:

imglink1

2.2. 解析

很明顯,我這裏創建了兩個四邊形,並且將其放到一個Mesh下。創建頂點屬性我使用的是簡單接口,創建頂點索引屬性信息使用的是高級接口。關鍵點在於對SubMesh的描述:

mesh.subMeshCount = 2;
SubMeshDescriptor subMeshDescriptor1 = new SubMeshDescriptor(0, 6);
mesh.SetSubMesh(0, subMeshDescriptor1, flags);

SubMeshDescriptor subMeshDescriptor2 = new SubMeshDescriptor(6, 6);
mesh.SetSubMesh(1, subMeshDescriptor2, flags);

SubMeshDescriptor類定義了從那個頂點索引開始,之後多長的空間是一個SubMesh,也就是對Mesh做了一個劃分。另外,GameObject上掛接的材質個數也要對應:

MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
if (meshRenderer == null)
{
    meshRenderer = gameObject.AddComponent<MeshRenderer>();
}

Material[] materials = new Material[2];       
materials[0] = material1;
materials[1] = material2;
meshRenderer.materials = materials;

MeshRenderer上能掛接多個材質,有多少個SubMesh就應該有多少個材質,它們是一一對應的。數量沒對應上Unity編輯器會報錯。

通過劃分SubMesh的方式來描述一個Mesh通常是用於存在多個材質的情況,如果使用的都是同一個材質,就最好不要作SubMesh劃分。我們打開Frame Debug,可以看到:
imglink2

一個Mesh分成了居然兩個渲染指令來實現!原因在於圖像引擎通常是一個狀態機,一個材質需要對應一個渲染指令,這就是爲什麼我們往往要儘可能複用材質,減少不同材質的個數。

3. 參考

  1. Unity3D學習筆記4——創建Mesh高級接口
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章