MRが楽しい

MRやVRについて学習したことを書き残す

UnityEngineのMeshクラスを読み解く

本日は Unity の調査枠です。
UnityEngine の Mesh クラスのドキュメントを理解のため、読み進めます。

Meshクラスのドキュメント

以下のドキュメントを読み進めていきます。
docs.unity3d.com

Mesh

説明

スクリプトからメッシュの作成や変更をできるようにするクラスです。
メッシュには頂点データ(位置、法線、UV座標など)と、面データが含まれます。
面はほとんどの場合、三角形です。

全ての頂点関連のデータは同じサイズの個別の配列に格納されます。
例えば、100個の頂点の Mesh があり、各頂点の座標、法線、および2つのテクスチャ座標が必要な場合についてです。
Mesh は[頂点(vertices)]、[法線(normals)]、[uv]、[uv2]の配列を持ち、それぞれの配列サイズは 100 になります。
i 番目の頂点に関連するデータは、同じく各配列の i 番目に格納されています。

Mesh-vertices - Unity スクリプトリファレンス
Mesh-normals - Unity スクリプトリファレンス
Mesh-uv - Unity スクリプトリファレンス
Mesh-uv2 - Unity スクリプトリファレンス

全ての頂点は座標、法線、接線、色、および最大8つのテクスチャ座標の情報を持ちます。
ほとんどの場合、テクスチャ座標は2次元データ(Vector2)ですが、3次元(Vector3)または4次元(Vector4)にすることも可能 です。
これは、シェーダーで使用される特殊効果のために、メッシュの頂点に任意のデータを保持するためによく使用されます。
スキニングされたメッシュの場合、頂点データにはボーンウェイト(boneWeights)を含めることもできます。

Mesh-boneWeights - Unity スクリプトリファレンス

メッシュの面のデータ(構成する三角形)は、三角形の面ごとに3つの頂点インデックス(triangles)で保持されます。
例えば、メッシュに10個の三角形がある場合、三角形の配列は30個の数値である必要があり、各数値は使用する頂点を示します。
頂点インデックス(triangles)配列の最初の3つの要素は、その三角形を構成する頂点のインデックスです。
次の3つの要素は別の三角形を構成します。

Mesh-triangles - Unity スクリプトリファレンス

三角形の面を用いる Mesh が最も一般的なユースケースです。
ただし、Unity はラインメッシュやポイントメッシュなど、他のメッシュトポロジタイプもサポートしています。
ラインメッシュの場合、各ラインは2つの頂点インデックスなどで構成されます。
SetIndices および MeshTopology を参照してください。

Mesh-SetIndices - Unity スクリプトリファレンス
MeshTopology - Unity スクリプトリファレンス

Simple Mesh API と Advanced Mesh API

Mesh クラスにはスクリプトから Mesh にデータを割り当てるための2組のメソッドがあります。

Simple Mesh API のメソッドは、インデックス、三角形、法線、接線などを設定するための基礎を提供します。
本メソッドには、例えば、範囲外のインデックスを含むデータを渡していないことを確認するための検証チェックが含まれます。
Unity のスクリプトからメッシュデータを割り当てる標準的なメソッドを提供します。

Simple Mesh API のメソッドは以下のとおりです。
SetColors、SetIndices、SetNormals、SetTangents、SetTriangles、SetUVs、SetVertices、SetBoneWeights

Mesh-SetColors - Unity スクリプトリファレンス
Mesh-SetIndices - Unity スクリプトリファレンス
Mesh-SetNormals - Unity スクリプトリファレンス
Mesh-SetTangents - Unity スクリプトリファレンス
Mesh-SetTriangles - Unity スクリプトリファレンス
Mesh-SetUVs - Unity スクリプトリファレンス
Mesh-SetVertices - Unity スクリプトリファレンス
Mesh-SetBoneWeights - Unity スクリプトリファレンス

Advanced Mesh API はより高度なメソッドセットです。
チェックまたは検証を実行するかどうかを制御して、メッシュデータに直接書き込むことができます。
本メソッドは、最大のパフォーマンスを必要とする高度なユースケースを対象としています。
高速ですが、データのチェックをスキップします。
本メソッドを使用する場合、Unityがチェックしないため、無効なデータを提供していないことを確認する必要があります。

Advanced Mesh API のメソッドは以下のとおりです。
SetVertexBufferParams, SetVertexBufferData, SetIndexBufferParams, SetIndexBufferData, SetSubMesh, MeshUpdateFlags

これらを使用して実行または省略されるチェックまたは検証を制御できます。

Mesh-SetVertexBufferParams - Unity スクリプトリファレンス
Mesh-SetVertexBufferData - Unity スクリプトリファレンス
Mesh-SetIndexBufferParams - Unity スクリプトリファレンス
Mesh-SetIndexBufferData - Unity スクリプトリファレンス
Mesh-SetSubMesh - Unity スクリプトリファレンス
MeshUpdateFlags - Unity スクリプトリファレンス

スクリプトからのメッシュの操作

Mesh API を使用する場合、3つの一般的なタスクがあります。

1.メッシュを最初から作成する

常に以下の順序で実行する必要があります。

a) 頂点(vertices)を割り当てる。
b) 三角形(triangles)を割り当てる。

using UnityEngine;

public class Example : MonoBehaviour
{
    Vector3[] newVertices;
    Vector2[] newUV;
    int[] newTriangles;

    void Start()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;
        mesh.vertices = newVertices;
        mesh.uv = newUV;
        mesh.triangles = newTriangles;
    }
}
2.フレームごとの頂点属性の変更

a)頂点(vertices)を取得する。
b)それらの属性を変更する。
c)メッシュに割り当てる。

using UnityEngine;

public class Example : MonoBehaviour
{
    void Update()
    {
        Mesh mesh = GetComponent<MeshFilter>().mesh;
        Vector3[] vertices = mesh.vertices;
        Vector3[] normals = mesh.normals;

        for (var i = 0; i < vertices.Length; i++)
        {
            vertices[i] += normals[i] * Mathf.Sin(Time.time);
        }

        mesh.vertices = vertices;
    }
}
3.メッシュの三角形と頂点を継続的に変更する

a) Clearメソッドを呼び出してリフレッシュする。
b) 頂点(vertices)とその他の属性を割り当てる。
c) 三角形(triangle)のインデックスを割り当てる。

新しい頂点(vertices)または三角形(triangle)を割り当てる前に、Clearメソッドを呼び出すことが重要です。
Unityは指定された三角形(triangle)のインデックスが範囲外の頂点を参照していないかどうかを常にチェックします。
Clearメソッドを呼び出してから、頂点(vertices)を割り当て、三角形(triangle)を割り当てると、範囲外の参照はありません。

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;
    }
}