MRが楽しい

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

UnityEngineのMeshクラスを読み解く Advanced Mesh API その1

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

前回記事の続きです。
bluebirdofoz.hatenablog.com

Advanced Mesh APIクラスのドキュメント

今回は Mesh クラスで利用する Advanced Mesh API の以下の関数ドキュメントを読み進めていきます。
docs.unity3d.com
docs.unity3d.com
docs.unity3d.com
docs.unity3d.com

SetVertexBufferParams

public void SetVertexBufferParams (int vertexCount, params VertexAttributeDescriptor[] attributes);

引数説明
vertexCountメッシュ内の頂点の数
attributes頂点データのレイアウト(属性、データタイプなど)

頂点バッファーのサイズとレイアウトを設定します。

このメソッドは最大のパフォーマンスを目指す上級ユーザー向けに設計されています。
主にインデックスバッファ、頂点バッファ、メッシュサブセットデータの基本的なメッシュデータ構造で動作します。

この方法を使用すると、Unityはデータ検証をほとんど実行しないため、データが有効であることを確認する必要があります。
特に、以下の確認する必要があります。
・インデックスバッファに範囲外のインデックスが含まれていないこと。
・SubMesh インデックスの範囲が SetSubMesh を介して更新されていること。

Simple Mesh API と Advanced Mesh API の違いについては Mesh ページを参照してください。
bluebirdofoz.hatenablog.com

メッシュ属性の指定方法の詳細については VertexAttributeDescriptor を参照してください。
docs.unity3d.com

using UnityEngine;
using UnityEngine.Rendering;

public class Example : MonoBehaviour
{
    void Start()
    {
        var mesh = new Mesh();
        // specify vertex layout with:
        // - floating point positions,
        // - half-precision (FP16) normals with two components,
        // - low precision (UNorm8) tangents
        // 以下の情報を持つ頂点のレイアウトを指定します。
        // - 浮動小数点の座標
        // - 2軸の半精度(FP16)法線
        // - 低精度(UNorm8)接線
        var layout = new[]
        {
            new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
            new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float16, 2),
            new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.UNorm8, 4),
        };
        var vertexCount = 10;
        mesh.SetVertexBufferParams(vertexCount, layout);

        // ...later on SetVertexBufferData would be used to set the actual vertex data
        // この後、SetVertexBufferData を使用して実際の頂点データを設定します。
    }
}

SetVertexBufferData

public void SetVertexBufferData (NativeArray data, int dataStart, int meshBufferStart, int count, int stream, Rendering.MeshUpdateFlags flags);
public void SetVertexBufferData (T[] data, int dataStart, int meshBufferStart, int count, int stream, Rendering.MeshUpdateFlags flags);
public void SetVertexBufferData (List data, int dataStart, int meshBufferStart, int count, int stream, Rendering.MeshUpdateFlags flags);

引数説明
data頂点データ配列
dataStartコピー元のデータの最初の要素
meshBufferStartデータを受け取るメッシュ頂点バッファーの最初の要素
countコピーする頂点の数
streamデータを設定する頂点バッファストリーム(デフォルト 0)
flags関数の動作を制御するフラグ(詳細はMeshUpdateFlagsを参照)

メッシュの頂点バッファーのデータを設定します。

Simple Mesh API は頂点、法線などの個別の関数を使用して頂点データを設定します。
最大限のパフォーマンスを必要とする使用例については SetSubMesh、SetIndexBufferParams、SetIndexBufferData、SetVertexBufferParamsなどの Advanced Mesh API を使用できます。
Advanced Mesh API はインデックスバッファ、頂点バッファ、メッシュサブセットデータで機能する基本的なメッシュデータ構造へのアクセスを提供します。

SetVertexBufferData を使用すると、各頂点属性のフォーマット変換を使用せずに、頂点データを直接設定できます。
設定されるデータレイアウトはメッシュの頂点データレイアウトと一致する必要があります。
(詳細は SetVertexBufferParams、GetVertexAttributes を参照)

dataStart、meshBufferStart、count パラメータを使用して、データの部分的な更新も可能です。

using UnityEngine;
using UnityEngine.Rendering;
using Unity.Collections;

public class Example : MonoBehaviour
{
    // Vertex with FP32 position, FP16 2D normal and a 4-byte tangent.
    // In some cases StructLayout attribute needs
    // to be used, to get the data layout match exactly what it needs to be.
    // FP32の座標、FP16の2D法線、4Byteの接線を持つ頂点の定義
    // 場合によっては StructLayout 属性が必要です
    // データレイアウトを正確に一致させるために使用します
    // https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.structlayoutattribute
    // https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.layoutkind
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    struct ExampleVertex
    {
        public Vector3 pos;
        public ushort normalX, normalY;
        public Color32 tangent;
    }

    void Start()
    {
        var mesh = new Mesh();
        // specify vertex count and layout
        // 頂点の数とレイアウトを指定します
        var layout = new[]
        {
            new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32, 3),
            new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float16, 2),
            new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.UNorm8, 4),
        };
        var vertexCount = 10;
        mesh.SetVertexBufferParams(vertexCount, layout);

        // set vertex data
        // 頂点データを設定します
        var verts = new NativeArray<ExampleVertex>(vertexCount, Allocator.Temp);

        // ... fill in vertex array data here...
        // ...ここで頂点配列データを入力... 

        mesh.SetVertexBufferData(verts, 0, 0, vertexCount);
    }
}

SetIndexBufferParams

public void SetIndexBufferParams (int indexCount, Rendering.IndexFormat format);

引数説明
indexCountインデックスバッファのサイズ
formatインデックスのフォーマット

インデックスバッファのサイズと形式を設定します

var mesh = new Mesh();

// setup vertex buffer data
// 頂点バッファデータをセットアップします
mesh.vertices = ...;

// set index buffer
// 新たなインデックスバッファを設定します
mesh.SetIndexBufferParams(...);
mesh.SetIndexBufferData(...);

// setup information about mesh subsets
//メッシュのサブセットに関する情報を再設定します
mesh.subMeshCount = ...;
mesh.SetSubMesh(index, ...);

インデックスバッファーのサイズまたは形式を変更すると subMeshCount は 1 に戻ります。
インデックスバッファーデータは初期化されません。
値を設定するには SetIndexBufferData を使用します。

SetVertexBufferParams

public void SetIndexBufferData (NativeArray data, int dataStart, int meshBufferStart, int count, Rendering.MeshUpdateFlags flags);
public void SetIndexBufferData (T[] data, int dataStart, int meshBufferStart, int count, Rendering.MeshUpdateFlags flags);
public void SetIndexBufferData (List data, int dataStart, int meshBufferStart, int count, Rendering.MeshUpdateFlags flags);

引数説明
dataインデックスバッファデータ配列
dataStartコピー元のデータの最初の要素
meshBufferStartデータを受け取るメッシュインデックスバッファーの最初の要素
countコピーするインデックスの数
flags関数の動作を制御するフラグ(詳細はMeshUpdateFlagsを参照)

メッシュのインデックスバッファのデータを設定します。

このメソッドは最大のパフォーマンスを目指す上級ユーザー向けに設計されています。
主にインデックスバッファ、頂点バッファ、メッシュサブセットデータの基本的なメッシュデータ構造で動作します。

この方法を使用すると、Unityはデータ検証をほとんど実行しないため、データが有効であることを確認する必要があります。
特に、以下の確認する必要があります。
・SubMesh インデックスの範囲外のデータが設定されていない事。
・トポロジが SetSubMesh を介して更新されていること

デフォルトでは、インデックスバッファの data 配列は範囲外のインデックスが含まれていないかチェックします。
パフォーマンスを重視する場合、flags パラメータを MeshUpdateFlags.DontValidateIndices に設定して、検証をオフにすることができます。

var mesh = new Mesh();

// setup vertex buffer data
// 頂点バッファデータをセットアップする
mesh.vertices = ...;

// set index buffer
// インデックスバッファを設定する
mesh.SetIndexBufferParams(...);
mesh.SetIndexBufferData(...);

// setup information about mesh subsets
// メッシュのサブセットに関する情報を設定する
mesh.subMeshCount = ...;
mesh.SetSubMesh(index, ...);

bluebirdofoz.hatenablog.com