MRが楽しい

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

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

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

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

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

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

SetSubMesh

public void SetSubMesh (int index, Rendering.SubMeshDescriptor desc, Rendering.MeshUpdateFlags flags);

引数説明
indexサブメッシュインデックス(詳細は subMeshCount を参照)
descサブメッシュデータ
flags関数の動作を制御するフラグ(詳細は MeshUpdateFlags を参照)

メッシュのサブメッシュに関する情報を設定します。

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

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

Simple Mesh API と Advanced Mesh API の違いについては Mesh ページを参照してください。
・UnityEngineのMeshクラスを読み解く
https://bluebirdofoz.hatenablog.com/entry/2020/08/02/223528

SubMeshDescriptor のバウンドボックス、SubMeshDescriptor の firstVertex および vertexCount の値は SetSubMesh によって自動的に計算されます。
ただし MeshUpdateFlags.DontRecalculateBounds フラグが渡された場合は計算しません。

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, ...);

各サブメッシュに設定するデータの詳細については、SubMeshDescriptor を参照してください。

サンプルスクリプト

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using Unity.Collections;

public class AdvancedExample : MonoBehaviour
{
    // FP32の座標を持つ頂点の定義
    // 場合によっては 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;
    }

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

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

        // ... fill in vertex array data here...
        // ...ここで頂点配列データを入力...
        ExampleVertex vert0 = new ExampleVertex();
        vert0.pos = new Vector3(0.0f, 1.0f, 0.0f);
        verts[0] = vert0;
        ExampleVertex vert1 = new ExampleVertex();
        vert1.pos = new Vector3(1.0f, -1.0f, -1.0f);
        verts[1] = vert1;
        ExampleVertex vert2 = new ExampleVertex();
        vert2.pos = new Vector3(-1.0f, -1.0f, -1.0f);
        verts[2] = vert2;
        ExampleVertex vert3 = new ExampleVertex();
        vert3.pos = new Vector3(0.0f, -1.0f, 1.0f);
        verts[3] = vert3;

        // 頂点配列データをバッファに設定
        mesh.SetVertexBufferData(verts, 0, 0, vertexCount);

        // インデックスバッファのサイズを設定
        int indexCount = 12;
        mesh.SetIndexBufferParams(indexCount, IndexFormat.UInt32);

        // ...ここでインデックスデータを入力...
        int[] indexlist = {
            0, 1, 2,
            0, 2, 3,
            0, 3, 1,
            3, 2, 1,
        };

        // インデックス配列データをバッファに設定
        mesh.SetIndexBufferData(indexlist, 0, 0, indexCount, MeshUpdateFlags.Default);


        // サブメッシュのサイズを設定
        mesh.subMeshCount = 1;

        // メッシュのサブメッシュに関する情報を設定する
        SubMeshDescriptor desc = new SubMeshDescriptor(0, indexCount, MeshTopology.Triangles);
        mesh.SetSubMesh(0, desc, MeshUpdateFlags.Default);

        // 作成したメッシュをメッシュフィルターに設定する
        MeshFilter meshFilter = GetComponent<MeshFilter>();
        meshFilter.mesh = mesh;
    }
}

f:id:bluebirdofoz:20200807065019j:plain

MeshUpdateFlags

メッシュデータの更新フラグです。

以下の Advanced Mesh API は関数の動作を制御するオプションとして本パラメータを取ります。
・SetVertexBufferData
・SetIndexBufferData
・SetSubMesh
本フラグを使用すると、メッシュのデータが更新されたときの動作を制御できます。

デフォルトではこれらのメソッドを使用するときに、指定されたデータのチェックと検証を実行します。
例えば、インデックス配列に範囲外の値があるかどうかをチェックします。

パフォーマンスを向上させる目的でフラグを使用すると、これらのチェックの一部または全てを省略できます。
これらのチェックを省略する場合は設定するデータが有効であることを確認する必要があります。

以下のように論理 OR 演算子を使用して、個々のフラグを組み合わせることができます。

MeshUpdateFlags.DontNotifyMeshUsers | MeshUpdateFlags.DontValidateIndices

変数

引数説明
Defaultメッシュデータを変更するとき、デフォルトのチェックと検証を実行します
DontValidateIndicesMesh.SetIndexBufferData を使用してメッシュのデータを変更するとき、インデックス値をチェックしないことを示します
DontResetBoneBoundsMesh.SetVertexBufferData または Mesh.SetIndexBufferData を使用してメッシュデータを変更するとき、スキニングされたメッシュボーン境界をリセットしないことを示します
DontNotifyMeshUsersメッシュデータを変更するとき、可能性のあるメッシュ境界の変更についてレンダラーコンポーネントに通知しないことを示します
DontRecalculateBoundsMesh.SetSubMeshを使用してメッシュデータを設定するとき、境界を再計算しないことを示します

docs.unity3d.com

SubMeshDescriptor

Mesh の単一のサブメッシュに関する情報が含まれています。

Simple Mesh API は Mesh.triangles、Mesh.vertices などの関数を使用します。
最大のパフォーマンスを必要とする Advanced Mesh API では、Mesh.SetSubMesh、Mesh.SetIndexBufferParams、Mesh.SetIndexBufferDataなどの関数を使用します。

Advanced Mesh API はインデックスバッファ、頂点バッファ、メッシュサブセットデータで機能する基本的なメッシュデータ構造へのアクセスを提供します。

1つのサブメッシュは、1つのマテリアルを使用しているメッシュの一部を表します。
多くのメッシュは1つのマテリアルのみを使用しますが、複数使用する場合もあります。
サブメッシュの情報は、次のもので構成されています。

indexStart

このサブセットの面インデックスデータが見つかるメッシュインデックスバッファ全体の開始点です。
Mesh.SetIndexBufferParams および Mesh.SetIndexBufferData を参照してください。

indexCount

このサブメッシュのインデックス数です。
例えば、三角形トポロジのメッシュではそれぞれの三角形の面に3つのインデックスが必要です。

topology

このサブメッシュのトポロジを示します。
最もよく利用されるのは三角形を示す MeshTopology.Triangles です。

baseVertex

最終的な頂点インデックスを計算するために、インデックスバッファの各値に追加されるオフセットです。

bounds

ローカル空間の頂点のバウンディングボックスです。

firstVertex and vertexCount

このサブメッシュのインデックスバッファによって参照される頂点の範囲です。

bounds、firstVertex、および vertexCount の値は Mesh.SetSubMesh によって自動的に計算されます。
ただし MeshUpdateFlags.DontRecalculateBounds フラグが渡された場合は計算しません。

変数

引数説明
baseVertex最終的な頂点インデックスを計算するため、インデックスバッファの各値に追加されるオフセット
boundsローカル空間の頂点の境界ボックス
firstVertexサブメッシュのインデックスバッファーの最初の頂点
indexCountサブメッシュの面データのインデックス数
indexStartメッシュインデックスバッファ全体の開始点
topologyサブメッシュの面トポロジ
vertexCountサブメッシュのインデックスバッファーで使用される頂点の数

docs.unity3d.com