Unity3D學習筆記4——建立Mesh高階介面

2022-07-02 21:00:39

1. 概述

在文章Unity3D學習筆記2——繪製一個帶紋理的面中使用程式碼的方式建立了一個Mesh,不過這套介面在Unity中被稱為簡單介面。與其相對應的,Unity還提供了一套高階API來建立Mesh。

2. 詳論

根據Unity檔案的論述,使用高階介面能夠得到更高的效能,能夠跳過一些驗證檢查。但是這並不是最關鍵的,簡單介面有個最大的缺點是頂點個數超過65535個時就有問題(至少在2019.4.3f1版本還是這樣)。

話不多說,直接上程式碼:

using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
public class Note4Main : MonoBehaviour
{
    public Material material;

    // Start is called before the first frame update
    void Start()
    {
        Mesh mesh = new Mesh();
        mesh.name = "quad";

        //頂點資料
        VertexAttributeDescriptor[] vertexAttributeDescriptorList = new[]{
             new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
             new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3),
             new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32, 2)};

        const int vertexCount = 4;
        const int verticesAttributeBufferLength = vertexCount * (3 + 3 + 2);
        float[] verticesAttributeBuffer = new float[verticesAttributeBufferLength] {
            -5, -5, 0, 0, 0, -1,0, 0,
            -5, 5, 0, 0, 0, -1, 0, 1,
            5, -5, 0, 0, 0, -1, 1, 0,
            5, 5, 0, 0, 0, -1, 1, 1
        };

        mesh.SetVertexBufferParams(vertexCount, vertexAttributeDescriptorList);
        mesh.SetVertexBufferData(verticesAttributeBuffer, 0, 0, verticesAttributeBufferLength, 0);

        int[] triangles = new int[6] { 0, 1, 2, 1, 3, 2 };
        int indexCount = triangles.Length;

        //頂點索引檔案
        mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);
        mesh.SetIndexBufferData(triangles, 0, 0, indexCount);

        //子Mesh描述
        mesh.subMeshCount = 1;
        SubMeshDescriptor subMeshDescriptor = new SubMeshDescriptor(0, indexCount);
        mesh.SetSubMesh(0, subMeshDescriptor);

        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>();
        }
        meshRenderer.material = material;
    }

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

最後可以直接得到與Unity3D學習筆記2——繪製一個帶紋理的面一樣的效果。如果有一些圖形基礎,就會很容易理解這段程式碼。都是申請一個buffer,定義頂點的描述資訊,這裡是按照x,y,z,nx,ny,nz,u,v的順序,一個頂點一個頂點進行排列。接著是定義一個頂點索引buffer;不同的是增加了一個對於子mesh的描述。在Unity裡,一個Mesh可以包含多個子Mesh,每個子Mesh都能對應MeshRenderer中的多個材質中的一個。

3. 其他

  1. 根據官方檔案論述,這套高API效能更高。但個人使用感覺不是很明顯。跳過驗證的設定也可能帶來一些其他問題,我一般用預設設定。
  2. 另一個優點是,可以避免簡單介面中頂點個數超過65535時Mesh繪製不正確的問題。理論上,繪製的批次越少越好,這就要求儘可能合批次繪製,同樣頂點個數的物體分多個mesh繪製,效能比不上使用一個大的Mesh一次繪製。
  3. 官方檔案還提到了有其他介面可以通過C# Jobs和Burst建立Mesh,C# Jobs與多執行緒相關,難道意味著可以在多執行緒下建立Mesh了?有待進一步研究。

4. 參考

  1. Unity3D學習筆記2——繪製一個帶紋理的面
  2. Unity Documentation - Mesh