渋谷ほととぎす通信

完全趣味でやってるUnityメモ。説明できないところを説明できるようにするための個人ブログ。昨日の自分より少しでも大きくなれるように。。。 ※所属団体とは一切関係がありません

Unity 動的にメッシュを生成してゴニョゴニョする : 超基本編


f:id:esakun:20150730215258g:plain

仕事で実践することはほぼ無いのですが、無性にUnityでメッシュを動的に生成したくなりました。
色々調べてみるとUnityでもメッシュは作成できるという事を知り「実際にやってみよう!」という記事です。

とりえず三角ポリゴンを1つ表示してみる

WebGLのHelloWorldと同様、三角ポリゴン1つを表示させてみることがすべての始まりです。

esakun.hateblo.jp

メッシュというのは、一般的にポリゴンメッシュの事を指します。 ポリゴンメッシュとは3Dオブジェクトを表す「頂点・辺・面」の集合体の事を指します。

一般的に表示できるメッシュは3点もしくは4点を結んだ三角形メッシュ四角形メッシュです。Unityは三角形メッシュと四角形メッシュに対応しています。

f:id:esakun:20151129152423p:plain:w200

こんな感じの1辺2メートルの三角形メッシュを表示してみます。
(Unityの単位は1メートル)

とりあえずソースから。
このソースをGameObjectにAddComponentして実行すると三角形が描画されます。

DynamicCreateMesh.cs

using UnityEngine;
using System.Collections;

[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshFilter))]
public class DynamicCreateMesh : MonoBehaviour
{
    private void Start ()
    {
        var mesh = new Mesh ();
        mesh.vertices = new Vector3[] {
            new Vector3 (0, 1f),
            new Vector3 (1f, -1f),
            new Vector3 (-1f, -1f),
        };
        mesh.triangles = new int[] {
            0, 1, 2 
        };

        var filter = GetComponent<MeshFilter> ();
        filter.sharedMesh = mesh;
    }
}

1つ1つ処理の内容を見ていきましょう。

1. Meshクラスでメッシュを生成

動的にメッシュを作るためにはMeshインスタンスが必要です。

var mesh = new Mesh ();
mesh.vertices = new Vector3[] {
    new Vector3 (0, 1f),
    new Vector3 (1f, -1f),
    new Vector3 (-1f, -1f),
};
mesh.triangles = new int[] {
    0, 1, 2  
};

Meshクラスをnewキーワードで生成していますが、2つの要素が必要です。

  • 頂点座標配列
  • 頂点を結ぶ順番配列

頂点座標(verticesプロパティ)は言葉の通りメッシュを構築する頂点の3D座標です。

頂点を結ぶ順番(trianglesプロパティ)はメッシュを正しく表示させるためにとても重要な情報です。
Unityは左手座標系なので、時計回りに頂点を結んだ面が前面(法線の向き)になります。特に設定をいじらなければ前面にメッシュが表示されます。
もしメッシュが表示されない、表示がおかしいなどあれば、この頂点を結ぶ順番を確認してみても良いかもしれません。
※ちなみにOpenGL系のグラフィックAPIでは右手座標系なので真逆です。

2. メッシュを表示するために必要なもの

メッシュを表示させるためにはMeshFilterコンポーネントMeshRendererコンポーネントが必要です。

UnityのマニュアルMeshFilterの説明がとても少なく推測レベルですが、MeshFilterとはメッシュ情報をレンダラーに渡す役割を果たすために存在していると思われます。

「直接レンダラーにメッシュを渡せばいいのでは?」

と思ったのですが、メッシュのレンダラーにはMeshRendererSkinnedMeshRendererの2種類が存在します。
簡単に説明するとSkinnedMeshRendererはメッシュの頂点がボーンの影響で動く場合です。

一方MeshRendererはメッシュ自体の形は変わらない場合に使用されるレンダラーです。

どちらのレンダラーを使うか割り振りをするのがMeshFilterコンポーネントなのではないかと思います。
試しにMeshFilterにメッシュを渡した後、MeshFilterをDestroyしてもメッシュは正しく表示されます。

var filter = GetComponent<MeshFilter> ();
filter.sharedMesh = mesh;

MeshFilterに先ほど作成したMeshインスタンスを渡します。

また、MeshFilterコンポーネントとMeshRendererコンポーネントはメッシュを表示するためには必ず必要なので、RequeireComponentアトリビュートを使ってDynamicCreateMeshコンポーネントをAddComponentした時に自動でアタッチするようにしています。

[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshFilter))]

無事に表示されました。

f:id:esakun:20151129153339p:plain:w200

メッシュを着色してみる

三角形ポリゴンは表示されましたが、ピンク色になってしまいました。
Materialの設定がない場合にこういうアラート表示になります。

f:id:esakun:20151129162857p:plain

ProjectのCreateボタンからSampleMaterialという名前でMaterialを作成します。

f:id:esakun:20151129162853p:plain

ShaderをUnlit/Colorに設定し、Main Colorに適当な色を設定してみます。

f:id:esakun:20151129162716p:plain

DynamicCreateMeshクラスのソースを一部変更します。
コチラ

using UnityEngine;
using System.Collections;

[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshFilter))]
public class DynamicCreateMesh : MonoBehaviour
{
    // 変更箇所 : Materialを保持するようにする
    [SerializeField]
    private Material _mat;

    private void Start ()
    {
        var mesh = new Mesh ();
        mesh.vertices = new Vector3[] {
            new Vector3 (0, 1f),
            new Vector3 (1f, -1f),
            new Vector3 (-1f, -1f),
        };
        mesh.triangles = new int[] {
            0, 1, 2 
        };

        var filter = GetComponent<MeshFilter> ();
        filter.sharedMesh = mesh;

        // 変更箇所 : MeshRendererからMaterialにアクセスし、Materialをセットするようにする
        var renderer = GetComponent<MeshRenderer> ();
        renderer.material = _mat;
    }
}

この状態で実行すると、このように黄色い三角形メッシュが表示されるようになりました。

f:id:esakun:20151129163335p:plain:w200

画像を貼ってみる

f:id:esakun:20151129164400p:plain:w200

こんな形のテクスチャをメッシュに貼り付けてみようと思います。

f:id:esakun:20151129164546p:plain:w200

先ほど作ったSampleMaterialを選択し、シェーダをUnlit/Textureに変更してテクスチャを設定します。

実行!!

f:id:esakun:20151129164930p:plain:w200

すると予想に反して青い三角形が表示されます。

UV座標の設定

なぜテクスチャが表示されず青い三角形が表示されたのかというと、各頂点に対してUV座標が設定されていなかったからです。

テクスチャを貼る場合は各頂点に対しUV座標を設定する必要があります。

using UnityEngine;
using System.Collections;

[RequireComponent (typeof(MeshRenderer))]
[RequireComponent (typeof(MeshFilter))]
public class DynamicCreateMesh : MonoBehaviour
{
    [SerializeField]
    private Material _mat;

    private void Start ()
    {
        var mesh = new Mesh ();
        mesh.vertices = new Vector3[] {
            new Vector3 (0, 1f),
            new Vector3 (1f, -1f),
            new Vector3 (-1f, -1f),
        };
        mesh.triangles = new int[] {
            0, 1, 2 
        };

        // 修正箇所 : 各頂点に対してUV座標を設定しています
        mesh.uv = new Vector2[] {
            new Vector2 (0.5f, 1f),
            new Vector2 (1f, 0),
            new Vector2 (0, 0),
        };

        var filter = GetComponent<MeshFilter> ();
        filter.sharedMesh = mesh;

        var renderer = GetComponent<MeshRenderer> ();
        renderer.material = _mat;
    }
}

修正箇所はmeshのuvプロパティVector2型の配列を渡しているところです。

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

これが何を指すかを説明していきます。

f:id:esakun:20151129172803p:plain

UV座標とは貼り付けるテクスチャの座標のことで、原点(0,0)は画像の左下、右上が(1,1)となり横がU座標、縦がV座標で、0〜1の範囲です。

先のuvプロパティに代入しているVector2型の配列は画像の三角形が丁度収まるようにUV座標を設定しています。

f:id:esakun:20151129174004p:plain

では、次のようなUV座標を代入してみるとどうなるでしょうか。

float texSize = 256f;
mesh.uv = new Vector2[] {
    new Vector2 (86f / texSize, 100f / texSize),
    new Vector2 (116f / texSize, 42f / texSize),
    new Vector2 (60f / texSize, 42f / texSize),
};

f:id:esakun:20151129174136p:plain

このようにUV座標で指定した部分がメッシュに描画されます。

最終的なソースはコチラ

DynamicCreateMesh.cs · GitHub

まとめ

本記事では、以下の事をやってきました。

  1. 三角形メッシュを生成
  2. カラーを変更
  3. UV座標を設定してテクスチャを描画する

3Dソフトが勝手にやってくれていた部分(頂点座標やUV座標の設定)を改めて自分でフルスクラッチすることで、3Dソフトがいかに便利なのかがよく分かった気がします。まだまだ知らない部分が多いので、少しずつこのブログにまとめていく予定です。非常に初心者向けの記事になりそうですが、気にせず自分の知識の整理をしていこうと思います。

あわせてどうぞ

www.shibuya24.info

「動的メッシュ生成」シリーズ記事

esakun.hateblo.jp esakun.hateblo.jp esakun.hateblo.jp esakun.hateblo.jp