【Unity】動的にMeshを作る!

Post : 2022/1/10 Update : 2022/1/10

あるサイズでMeshを作りたいときはBlenderなどの3DCGソフトでfbx出力をして持ってくることが多いが,プログラム内でサイズを決めたいときなどはそうもいかない。

MeshとMeshRendererについて

そもそも、Unity上で3DモデルがレンダリングするときMeshFilterMeshRendererのコンポーネントを使用している。

MeshFilter側でMeshデータをSceneに取り込み、レンダリング時にはMeshRendererはMeshFilterのMeshデータとMaterialから画面に描画している感じだ。

今回は、MeshFilter側のMeshプロパティを動的に変更する。

Meshを作成

Meshを構成する際の最低限の情報は「頂点がどこにあるか」という位置情報(Vertex)と「面がどの頂点で作られているのか」面情報(Polygon)が必要になる。Unityでは三角形で1つの面が構成されている。

サンプルで今回は4つの頂点情報と2つの三角形からなる四角形(UnityのQuad)を作ってみる。

頂点情報の配列を定義

まずは4つの頂点情報をこんな感じでVertex3型の配列で定義する。

Vector3[] vertices = {
    new Vector3(-0.5f, -0.5f, 0),
    new Vector3(-0.5f,  0.5f, 0),
    new Vector3( 0.5f,  0.5f, 0),
    new Vector3( 0.5f, -0.5f, 0)
};

順番は左下、左上、 右上、右下。後ほどこの順番が必要になってくる。

面情報の配列を定義

Unityでは三角形で一つの面が形成されるので、三角形(0,1,2)と三角形(0,1,3)をつなげて(0,1,2,0,1,3)の順番でint型の配列を定義する。

int[] triangles = { 0, 1, 2, 0, 2, 3 };

Meshクラスの生成

Unity標準のMeshクラス使用する。

といっても既に頂点情報と面情報は作成済みなので、空のメッシュを作成して、verticesに頂点情報をtrinalgesに面情報を渡す。

Mesh[] quadMesh = new Mesh();
quadMesh.vertices = vertices;
quadMesh.triangles = triangles;

Meshを表示してみる

作成したMeshをMeshFilterコンポーネントに渡して実際に表示してみる。


MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
if (!meshFilter) meshFilter = gameObject.AddComponent<MeshFilter>();

MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
if (!meshRenderer) meshRenderer = gameObject.AddComponent<MeshRenderer>();

meshFilter.mesh = quadMesh;
meshRenderer.sharedMaterial = mat;

ついでにMeshFilterとMeshRendererもプログラム側から追加!

?!?!Unity標準のマテリアルと比較すると何かが違う。

法線情報の追加

メッシュの描画ができる状態にはなったが一切ライティングがされていない状態だ。

最初にMeshを構成する際の最低限の情報は位置情報(Vertex)と面情報(Polygon)が必要と言ったが、ライティングをする上で面の向き(法線)が必要になる。頂点にも法線(それぞれの頂点に隣接する面の法線情報(法線ベクトル)を足し合わせたものが頂点の法線)が存在する。

Quadの頂点の法線は図のような感じで

Meshに法線を渡す際は頂点の配列と同じ順番で


Vector3[] normals = {
    new Vector3(0f, 0f, -1f),
    new Vector3(0f, 0f, -1f),
    new Vector3(0f, 0f, -1f),
    new Vector3(0f, 0f, -1f)
};

と法線情報を定義する。

さっき作ったMeshに法線を渡すと

quadMesh.normals = normals;

無事同じ見た目になった。

先ほど法線情報を定義したがUnityでは頂点と面から自動で法線を計算してくれるメソッドが存在する。


//mesh.normals = normals;
mesh.RecalculateNormals();

法線情報を特別定義する必要がない場合はこちらを使うほうがおススメ!

UV情報を追加

テクスチャを適応する際はUV情報が必要になる。

図のように左下基点でUV情報を定義して


Vector2[] uv = {
    new Vector2(0f, 0f),
    new Vector2(0f, 1f),
    new Vector2(1f, 1f),
    new Vector2(1f, 0f)
};

MeshにUVを渡すと

mesh.uv = uv;

無事テクスチャが表示された。

以上が動的にMeshを追加する方法でした!

コード全文

using UnityEngine;

public class MeshGenerator : MonoBehaviour
{
    [SerializeField] Material mat;
    Vector3[] vertices = {
        new Vector3(-0.5f, -0.5f, 0),
        new Vector3(-0.5f,  0.5f, 0),
        new Vector3( 0.5f,  0.5f, 0),
        new Vector3( 0.5f, -0.5f, 0)
    };

    int[] triangles = { 0, 1, 2, 0, 2, 3 };

    /*Vector3[] normals = {
        new Vector3(0f, 0f, -1f),
        new Vector3(0f, 0f, -1f),
        new Vector3(0f, 0f, -1f),
        new Vector3(0f, 0f, -1f)
    };*/

    Vector2[] uv = {
        new Vector2(0f, 0f),
        new Vector2(0f, 1f),
        new Vector2(1f, 1f),
        new Vector2(1f, 0f)
    };

    void Start()
    {
        Mesh quadMesh = new Mesh();
        quadMesh.vertices = vertices;
        quadMesh.triangles = triangles;

        //quadMesh.normals = normals;
        quadMesh.uv = uv;
        quadMesh.RecalculateNormals();

        MeshFilter meshFilter = gameObject.GetComponent<MeshFilter>();
        if (!meshFilter) meshFilter = gameObject.AddComponent<MeshFilter>();

        MeshRenderer meshRenderer = gameObject.GetComponent<MeshRenderer>();
        if (!meshRenderer) meshRenderer = gameObject.AddComponent<MeshRenderer>();

        meshFilter.mesh = quadMesh;
        meshRenderer.sharedMaterial = mat;

    }
}

Meshの修正がC#でできるあたりが、わざわざ3Dデータを修正後再インポートしなくてもいのが個人的に重宝してます。

もし質問などあったらTwitterのDMまで~