夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

Unityで複数のオブジェクトをマテリアルスロットを維持して統合する

本日はUnity枠です。

昨日Unityでメッシュの統合を行うコードを実行しました。

redhologerbera.hatenablog.com

このコードでは2つのメッシュを統合して新しいメッシュを生成していますが、マテリアルの処理をしておらず、片方のマテリアルが統合後のマテリアルとして使用されます。

今回はマテリアルもそれぞれ統合してマテリアルスロットに追加できるようにします。

〇コード

今回は次のようなコードで実現しました。

using UnityEngine;

public class MeshCombiner : MonoBehaviour
{
    void Start()
    {
        // 2つのメッシュとマテリアルを取得
        MeshFilter meshFilter1 = GameObject.Find("Mesh1").GetComponent<MeshFilter>();
        MeshFilter meshFilter2 = GameObject.Find("Mesh2").GetComponent<MeshFilter>();

        // メッシュとマテリアルごとにCombineInstanceを作成
        CombineInstance[] combine1 = new CombineInstance[meshFilter1.mesh.subMeshCount];
        for (int i = 0; i < meshFilter1.mesh.subMeshCount; i++)
        {
            combine1[i].mesh = meshFilter1.mesh;
            combine1[i].subMeshIndex = i;
            combine1[i].transform = meshFilter1.transform.localToWorldMatrix;
        }

        CombineInstance[] combine2 = new CombineInstance[meshFilter2.mesh.subMeshCount];
        for (int i = 0; i < meshFilter2.mesh.subMeshCount; i++)
        {
            combine2[i].mesh = meshFilter2.mesh;
            combine2[i].subMeshIndex = i;
            combine2[i].transform = meshFilter2.transform.localToWorldMatrix;
        }

        // CombineInstanceを統合
        CombineInstance[] finalCombine = new CombineInstance[combine1.Length + combine2.Length];
        combine1.CopyTo(finalCombine, 0);
        combine2.CopyTo(finalCombine, combine1.Length);

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(finalCombine, false, false);

        // 統合されたメッシュを新しいオブジェクトに適用
        GameObject combinedObject = new GameObject("CombinedMesh");
        combinedObject.AddComponent<MeshFilter>().mesh = combinedMesh;
        MeshRenderer combinedRenderer = combinedObject.AddComponent<MeshRenderer>();

        // マテリアルを適用
        Material[] materials = new Material[meshFilter1.GetComponent<MeshRenderer>().materials.Length + meshFilter2.GetComponent<MeshRenderer>().materials.Length];
        meshFilter1.GetComponent<MeshRenderer>().materials.CopyTo(materials, 0);
        meshFilter2.GetComponent<MeshRenderer>().materials.CopyTo(materials, meshFilter1.GetComponent<MeshRenderer>().materials.Length);
        combinedRenderer.materials = materials;

        // 元のメッシュを非アクティブ
        meshFilter1.gameObject.SetActive(false);
        meshFilter2.gameObject.SetActive(false);
    }
}

〇サブメッシュ

通常メッシュ(1ポリゴン)は頂点、法線、UV、メッシュのインデックス、マテリアルなどの情報で構成されます。

 この際に1つのメッシュに対して1つのマテリアルが基本てきに割り当てられます。

 このマテリアルごとのメッシュのグループをSubMeshと呼びます。 オブジェクトはマテリアルごとのSubMeshの集まりで構成されています。

・参考

redhologerbera.hatenablog.com

つまりマルチマテリアルのオブジェクトはSubMeshを定義して構成する必要があります。前回は単純にメッシュオブジェクトとしてConvineしていましたが、今回は以下のFor文でSubMeshを考慮しています。

 for (int i = 0; i < meshFilter1.mesh.subMeshCount; i++)
        {
            combine1[i].mesh = meshFilter1.mesh;
            combine1[i].subMeshIndex = i;
            combine1[i].transform = meshFilter1.transform.localToWorldMatrix;
        }

        CombineInstance[] combine2 = new CombineInstance[meshFilter2.mesh.subMeshCount];
        for (int i = 0; i < meshFilter2.mesh.subMeshCount; i++)
        {
            combine2[i].mesh = meshFilter2.mesh;
            combine2[i].subMeshIndex = i;
            combine2[i].transform = meshFilter2.transform.localToWorldMatrix;
        }

この処理ではそれぞれのメッシュについて、SubMeshのインデックスを取得して代入しています。

こうして得たデータを統合しています。

 CombineInstance[] finalCombine = new CombineInstance[combine1.Length + combine2.Length];
        combine1.CopyTo(finalCombine, 0);
        combine2.CopyTo(finalCombine, combine1.Length);

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(finalCombine, false, false);

メッシュに代入して最後にマテリアルスロットにそれぞれの元のマテリアルを代入しています。

注意点としてここでは統合元のメッシュがマテリアルを1つしか持っていない想定での処理になり、複数マテリアルを用いている場合はfor文を使用してすべてのマテリアルを拾う必要があります。

        // マテリアルを適用
        Material[] materials = new Material[meshFilter1.GetComponent<MeshRenderer>().materials.Length + meshFilter2.GetComponent<MeshRenderer>().materials.Length];
        meshFilter1.GetComponent<MeshRenderer>().materials.CopyTo(materials, 0);
        meshFilter2.GetComponent<MeshRenderer>().materials.CopyTo(materials, meshFilter1.GetComponent<MeshRenderer>().materials.Length);
        combinedRenderer.materials = materials;

以上でマテリアルスロットを維持して統合できました。

本日は以上です。