こんにちわ、Unityエンジニアのオオバです。

「Unityのスクリプトからメッシュを作りたくないですか?」

スクリプトからメッシュを作ることができれば、
自由自在にメッシュを操作し自分の思い描く3Dの世界を構築できます。

とはいえ千里の道も一歩から

今回は超基本編として
3Dプログラミング初心者向け の記事です。

実際に手を動かしながら
動的にメッシュを作ってみましょう。

3Dプログラミングの基礎「三角形」を極める

【超基本編】Unityで動的にメッシュを生成する方法_0

このシリーズは3ステップあります。

3Dプログラミングにおいて、
三角形がメッシュは最小単位です。

三角形を生成し、色を塗って画像を貼る。
この基礎中の基礎をまずはマスターしましょう。

途中シェーダーが登場しますが
心配無用です。凄くシンプルですから。

この最小単位のメッシュと描画の基礎を扱えるようになれば
あとは応用です

基本をおさえていきましょう。
本記事はステップ1の
「動的に三角形メッシュを作る」編 です。

まずはここから始めます。

この記事の内容

動的に三角形メッシュを作る4ステップ

【超基本編】Unityで動的にメッシュを生成する方法_1

三角形のメッシュ作成には
4ステップあります。

  1. メッシュの作成
  2. 頂点座標をメッシュにセット
  3. 頂点の順番をメッシュにセット
  4. メッシュの描画処理

前述の通り メッシュの最小単位は三角形 です。

三角形のメッシュを表示させることが、
「3Dプログラミングの基礎」
全てはここから始まります。

まずはUnityで三角形メッシュを1つ表示させていきます。

【準備】 描画する三角形の仕様確認

プログラミングに入る前に
三角形の仕様を確認しましょう。

【超基本編】Unityで動的にメッシュを生成する方法_2

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

※※Unityの単位はメートル

三角形を描画する方法はいくつかありますが、
今回は1つの三角形に対し1つのGameObjectを使う方法を採用しています。

では三角形メッシュを作っていきます。

1.メッシュの作成

【超基本編】Unityで動的にメッシュを生成する方法_3

まずはメッシュが必要です。

💻ソースコード : メッシュの作成処理
var mesh = new Mesh ();  

Meshクラスを使います。
newを使ってMeshインスタンスを作って下さい。

作成したMeshに、
次の情報をセットしていきます。

2.頂点座標をメッシュにセット

【超基本編】Unityで動的にメッシュを生成する方法_4

三角形を構成する頂点数は3点。
3点の頂点座標をメッシュに渡します。

3D空間上の座標のため、
頂点座標はVector3型(x, y, z)で定義。

また3つの頂点なので
Vector3型の配列で渡します。。

Meshクラスのverticesプロパティに
頂点座標の配列をセットします。

💻ソースコード : 頂点座標配列をメッシュにセット
mesh.vertices = new Vector3[] {  
 new Vector3 (0, 1f, 0),  
 new Vector3 (1f, -1f, 0),  
 new Vector3 (-1f, -1f, 0),  
};  

メッシュに渡す頂点座標配列は
GameObjectを原点にしたローカル座標です。
ここ重要です。

3.頂点の順番をメッシュにセット

【超基本編】Unityで動的にメッシュを生成する方法_5

メッシュの描画には頂点の順番情報が必要 です。
それがMeshクラスのtrianglesプロパティです。

頂点の順番を インデックス(Index)
その配列を インデックス配列 と呼びます。

trianglesにインデックス配列を渡していきましょう。

インデックス配列は
頂点の順番配列なのでint型の配列です。

💻ソースコード : インデックス配列をメッシュにセット
mesh.triangles = new int[] {  
 0, 1, 2  
};  

頂点を結ぶ順番は超大事

インデックス配列の数値は頂点を結ぶ順番です。
この数字と先の頂点情報は結びつきます。

【超基本編】Unityで動的にメッシュを生成する方法_6

頂点を結ぶ順番配列 (trianglesプロパティ)
メッシュを正しく表示させるためにとても重要な情報です。

メッシュには前と後ろがある

Unityは時計回りに頂点を結んだ面が です。
メッシュには前と後ろが存在します。

シェーダーの設定(カリング)がデフォルトであれば、
前面にメッシュが表示されます。

前面というのは法線(面の向き)を指定するということです。

このようなトラブルが発生した場合は、
この頂点を結ぶインデックス配列
またはシェーダーを見直すと良いでしょう。

***

メッシュ描画の話に戻します。
頂点座標配列、インデックス配列を
メッシュにセットしました。

準備は完了。残りは描画するだけです。

4.メッシュの描画

【超基本編】Unityで動的にメッシュを生成する方法_7

Unityでメッシュを描画するコンポーネントは2種類。

2つの違いは 頂点が個別に動くかどうか です。
布やキャラクタのような個別に頂点が動く場合は
SkinnedMeshRendererを使います。

SkinnedMeshRendererを使用している例

【超基本編】Unityで動的にメッシュを生成する方法_8

使用アセットRunner Action Animation Pack

今回は頂点が動かないためMeshRendererを使います。

【超基本編】Unityで動的にメッシュを生成する方法_9

このようにMeshRenderer
AddComponentしておきます。

MeshRendererにメッシュを直接渡したいところですが
Unityはそれを許しません。

MeshFilterコンポーネントを通して
MeshRendererに渡されます。

【超基本編】Unityで動的にメッシュを生成する方法_10

MeshFilterもAddComponentしておきましょう。

💻ソースコード : MeshFilterにメッシュをセット
var filter = GetComponent<MeshFilter> ();  
filter.sharedMesh = mesh;  

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

三角形メッシュ描画できた!

【超基本編】Unityで動的にメッシュを生成する方法_11

この段階ではピンク色の三角形になってしまいますが、
無事に表示されました!!

まとめ : Unityで動的にメッシュを生成する方法

【超基本編】Unityで動的にメッシュを生成する方法_12

超基本編としてUnityで動的にメッシュを
生成する方法を紹介しました。

オオバ
オオバ
具体的には三角形1つを描画する方法でしたね

記事の内容を簡単にまとめます。

  1. Meshインスタンスの作成
  2. 頂点座標配列を vertices にセット
  3. インデックス配列を triangles にセット
  4. MeshFilterにMeshを渡す
  5. MeshRendererでメッシュが描画される

こんな感じです。

コードは空で書けなくても
メッシュを描画する流れ
必要な情報のイメージ ができると良いでしょう。

これらは必ず必要です。
UnityではなくWebGLでも同じです。
ツールは関係なく応用可能です。

この基本をしっかり頭に叩き込んで、
メッシュ描画のイメージができると
3Dプログラミングの幅が広がります

次回はこの三角形に色を塗ります。
簡単なシェーダーが出てきますのでお楽しみに。

シェーダー学習の第一歩としても良い教材かも知れません。

サンプルコードの共有と補足解説

【超基本編】Unityで動的にメッシュを生成する方法_13

最後にソースコード全体を共有
ここで今回作ったソース全体を共有します。

💻ソースコード : 三角形メッシュの生成ソース全体 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  
      };  
      // 補足説明②  
      mesh.RecalculateNormals ();  
      // MeshFilterを通してメッシュをMeshRendererにセット  
      var filter = GetComponent<MeshFilter> ();  
      filter.sharedMesh = mesh;  
    }
}

今回紹介しなかった処理について補足解説します。

補足① : 事故防止のRequireComponent

クラス定義の前にRequireComponent
セットしています。

[RequireComponent (typeof(MeshRenderer))]  
[RequireComponent (typeof(MeshFilter))]  
public class DynamicCreateMesh : MonoBehaviour  

これは何かというと、
自動でAddComponentするコンポーネントの指定 です。

↑のコードは以下の内容を指します。

DynamicCreateMeshをAddComponentすると、
勝手にMeshRendererMeshFilter
同じGameObjectにAddComponentするということです。

メッシュ描画に必要なMeshRendererMeshFilterの2つが必要でした。
事前のAddComponent忘れを防止するトラブル回避の工夫ということです。

補足② : RecalculateNormals

描画の前にメッシュに対して
RecalculateNormalsを実行しています。

// 法線ベクトルの再計算処理  
mesh.RecalculateNormals();  

これは法線方向の再計算です。
前述の通りメッシュには前後があります。

RecalculateNormalsを呼ばないと
法線方向が (0, 0, 1) 固定となります。

つまり正しい面の方向を取得できないため、
今後ライティング処理などの際に意図しない表示になるかもしれません。

特に理由がない場合は必ず呼ぶでOKです。

では次回の記事で会いましょう。


フォローすると UIデザイナー力の上がるTwitter やってます!
今日から使えるテクニックを発信中。
ぜひフォローしてみてください!
👉フォローはこちら!

最後まで読んでいただきありがとうございました!
すばらしい動的メッシュライフをお過ごしください。

オススメ記事
検証環境
参考サイト