渋谷ほととぎす通信

完全趣味でやってる技術メモ。※所属団体とは一切関係がありません。

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


f:id:esakun:20150730215258g:plain:w450

仕事で実践することはほぼ無いですがUnityで動的にメッシュを生成してみます。「実際にやってみよう!」というかなり初心者向けの記事になります。

動的にメッシュを生成するシリーズの一貫です。

1.とりえず三角形メッシュを1つ表示してみる

僕は基礎学習を重要視しているため、WebGL、DirectXそれぞれべた書きでライブラリを使わず*1三角形描画をやりました。

生のWebGLで三角形描画
生のWebGLで三角形を描画するショートコード - 渋谷ほととぎす通信

C#で書くDirectXで三角形描画
C#でDirectX11 SlimDXで三角形を描画する - 渋谷ほととぎす通信

これらになぞられる形で今回も1個の三角形メッシュをUnityで動的に表示させてみます。

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

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

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

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

動的に三角形を描画する、MonoBehaviourを継承したDynamicCreateMeshクラスというものを作成しました。再生時に、このコンポーネントをGameObjectにAddComponentすると三角形が描画されます。

DynamicCreateMesh.cs

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

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

動的にメッシュを作るためにMeshインスタンスを作成します。

10行目辺り
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インスタンスには下記の2要素が必要です。

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

頂点座標配列(verticesプロパティ)は言葉の通りメッシュを構築する頂点の3Dローカル座標の配列です。
頂点を結ぶ順番配列(trianglesプロパティ)はメッシュを正しく表示させるためにとても重要な情報で、いわゆるインデックスバッファです。

Unityは時計回りに頂点を結んだ面が前面(法線の向き)になります。シェーダのカリング設定がデフォルトであれば前面にメッシュが表示されます。

もしメッシュが表示されない、表示がおかしいなどあれば、この頂点を結ぶ順番、またはシェーダを見直すと良いです。

19行目辺り
// 法線ベクトルの再計算処理
mesh.RecalculateNormals ();

またRecalculateNormalsメソッドで法線方向の再計算を行います。 これを呼ばないと法線方向が(0, 0, 1)固定になってしまうので、特に理由がない場合は必ず呼ぶことになるでしょう。

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

Unityでメッシュを表示させるためにはMeshRendererクラス、またはSkinnedMeshRendererクラスが必要です。今回のメッシュはスキニングされていないため、MeshRendererクラスを使用することになります。 ※スキニングメッシュとはボーンを使って頂点を動的に動かすことが出来るメッシュのことです。例えばUnity-chanはスキニングされたメッシュで動いています。

MeshRendererに加えMeshFilterクラスも必要です。

Mesh Filter はアセットからメッシュを取得し、画面上でのレンダリングするために、メッシュレンダラー に渡します。

メッシュフィルター - Unity マニュアルより

マニュアルに記載されているように、MeshFilterクラスはMeshデータをMeshRendererに渡すために必要です。メッシュデータを直接レンダラーに渡さない仕様にしているのは、MeshFilterを通すことでMeshRendererSkinnedMeshRendererをフィルタリングして事故を減らしてくれているのではないかと勝手に思っています。

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

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

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

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

f:id:esakun:20151129153339p:plain:w200
この段階ではピンク色になってしまいますが、三角形が無事に表示されました。

2.メッシュを着色する

三角形ポリゴンは表示されましたが、ピンク色になってしまいました。ポリゴンの色を設定する必要があります。

ここでシェーダが登場します。

シェーダとは「3Dオブジェクトをディスプレイに映し出すためのプログラム」です。このシェーダを使ってメッシュを着色します。

UnityではシェーダをMaterialというオブジェクトに内包します。MaterialをMeshRendererに渡してポリゴンの描画を行います。

ちなみに、UnityはMaterialの設定が無い場合、親切にこのようなアラート (ピンク色で描画) 表示をしてくれます。

シェーダについて以下の記事に分かりやすくまとめてみましたのでどうぞ。

余談になりますがWebGLやDirectXでは、シェーダを設定していないメッシュは何も表示されません。 何が原因なのかを探らねばならないためデバッグにとても時間を取られますが、Unityでは、先のようにピンク色でアラート表示してくれるため、原因がすぐわかります。このことからUnityは神だと言えます。

Materialの設定

Materialを使って、この三角形にシェーダを正しくアサインする作業をしていきます。

f:id:esakun:20190102025845p:plain
Project > Createボタン > MateiralsからSampleMaterialという名前でMaterialオブジェクトを作成します。

f:id:esakun:20151129162853p:plain

SampleMaterialを選択状態にして、ShaderをUnityのビルトインシェーダUnlit/Colorに設定し、Main Colorに適当な色を設定します。

ちなみにUnlit/Colorシェーダとは、ライト反映、陰影処理などしない、指定したカラーでピクセルを塗るだけのシンプルなシェーダです。

DynamicCreateMeshクラスのソースを一部変更し、Materialを反映できるようにします。

DynamicCreateMesh

f:id:esakun:20151129162716p:plain
SampleMaterialをDynamicCreateMeshコンポーネントにアタッチできるようになります。

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

頂点カラーを使った描画もあわせてどうぞ

3.画像を貼ってみる

頂点カラーで三角形を表示していましたが、テクスチャマッピングに移ります。

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

この画像(テクスチャ)をメッシュに貼り付けてみようと思います。


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

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

実行!!

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

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

UV座標の設定

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

テクスチャを貼る場合は各頂点に対しUV座標を設定する必要があるため、コードに変更を加えます。

DynamicCreateMesh.cs


修正箇所は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, 0.0)は画像の左下、右上が(1.0, 1.0)となり横がU座標、縦がV座標で、0.0〜1.0の範囲です。

先の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座標で指定した部分がメッシュに描画されます。

まとめ

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

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

個人的には、ほんの数行で三角形を描画できてしまったので、すごく簡単だなという印象です。

WebGLやDirectXをフルスクラッチで書くときには、頂点情報のバインド、テクスチャのロード処理、シェーダとのヒモ付などとてもとても面倒くさいのですが、Unityではそういった煩わしいことが一切なく三角形を描画できてしまうのは、繰り返しになりますがと言わざるを得ません。

こちらの記事もあわせてどうぞ
その他の動的にメッシュを生成するシリーズ

Unity動的にメッシュを生成するシリーズ - 渋谷ほととぎす通信

*1:DirectXはラッパーのSlimDXを使用しました