【連載】Unity時代の3D入門 – 第1回「ポリゴンを描画してみた」

Unity時代の3D入門

こんにちは。クライアントサイドエンジニアの矢野です。
この連載はタイトルの通り、Unityで3Dの基礎を勉強する内容となります。

「てっくぼっと!」では、これまでアプリボットの様々な取り組みを紹介してきましたが、弊社ではこのような取り組みの一環として、3Dの知識を持った人材の育成にも力を入れています。

僕も日々学ばせてもらっている一人なのですが、3Dを学び始めたときに思ったことが「専門用語多すぎ」「ポリゴン表示するだけで大変すぎ」「座標変換わからなすぎ」ということです。
さらに3D周りの技術はどんどん進歩するため、イチから追いつくことがますます難しくなっていきます。

しかし幸いなことに、現代にはUnityという便利なツールがあります。
せっかくUnityが色々とブラックボックスにしてくれているわけだから、ブラックボックスで良いものはいったんそのままにしておいて、徐々に学んでいこうというのがこの連載のアプローチです。

それではさっそく始めましょう。

 

描画のための準備1: 頂点座標

3Dで最も簡単に描画できるのは、ポリゴンと呼ばれる図形です。
ポリゴンを描画する際には、頂点と呼ばれる3D空間上の点を座標で定義します。
Unityの場合ポリゴンは三角形なので、ポリゴンを1つ描画する際には頂点座標を3つ定義します。

データとして持っているものはあくまでこの頂点座標であり、描画時にポリゴンの形が頂点座標から計算されます。

描画のための準備2: 頂点インデックス

頂点には頂点インデックスという番号が振られています。

ポリゴンを作るためにはそのポリゴンにどの頂点を使うのかを頂点インデックスで指定します。
これにより、例えば4つ頂点が定義されていた場合でも、任意の3点を使って三角形を作れます。

また、頂点インデックスを指定する順番にも意味があります。
ポリゴンには表面と裏面があり、順番に頂点座標を辿って、それが時計回りだったらポリゴンは表、反時計回りだったら裏となります(Unityの場合)。

設定にもよりますが、描画時には負荷を下げるために裏面しか見えないポリゴンは描画しません(背面カリング / バックフェイスカリングといいます)。
そのため、頂点インデックスはポリゴンの裏表を意識して正しく指定する必要があります。

描画のための準備3: 法線

法線は、ポリゴンの表面や頂点が向いている方向を表します。
例えば下図の場合、法線はz軸の負の方向を向いています。
また、法線は長さが1のベクトル(単位ベクトル)で表されます。

Unityで描画する

さて、ここまで理解すればあとはUnityで描画するだけです。
以下のスクリプトを適当なGameObjectにアタッチし、

using UnityEngine;

public class CreateTriangle : MonoBehaviour {

    [SerializeField]
    private Material _material;

    private Mesh _mesh;

    // (1) 頂点座標(この配列のインデックスが頂点インデックス)
    private Vector3[] _positions = new Vector3[]{ 
        new Vector3(0, 1, 0),
        new Vector3(1, -1, 0), 
        new Vector3(-1, -1, 0)
    };

    // (2) ポリゴンを形成する頂点インデックスを順番に指定する
    private int[] _triangles = new int[]{ 0, 1, 2 };

    // (3) 法線
    private Vector3[] _normals = new Vector3[]{ 
        new Vector3(0, 0, -1),
        new Vector3(0, 0, -1),
        new Vector3(0, 0, -1)
    };

    private void Awake () {
        _mesh = new Mesh();

        // (4) Meshに頂点情報を代入
        _mesh.vertices = _positions;
        _mesh.triangles = _triangles;
        _mesh.normals = _normals;

        _mesh.RecalculateBounds();
    }
    
    private void Update () {
        // (5) 描画
        Graphics.DrawMesh(_mesh, Vector3.zero, Quaternion.identity, _material, 0);
    }
}

_materialにインスペクタからUnity標準のDefault-Diffuseを設定し、再生します。
マテリアルに関しては現時点では理解しなくて問題ないので、説明は割愛します。

再生すると、ポリゴンが描画されることが確認できます。

処理の説明

上記では、まず (1) で正三角形を構成するように頂点座標を定義しています。

また、(2) はポリゴンを形成する頂点の、頂点インデックスを指定しています。
この頂点インデックスは (1) の配列のインデックスです。
また、裏表を意識して、z軸手前の方向(カメラ位置)から見たときに表面が見えるよう、順番に格納しています。

(3) では法線を定義しています。今回はどの頂点もz軸手前を向いているため、z軸手前方向の単位ベクトルを定義しています。

そして (4) でMeshオブジェクトを生成し、(1) から (3) の情報を代入しています。Meshは複数のポリゴンの情報を持つクラスです。
その下の_mesh.RecalculateBounds() は、今回理解する必要はありませんが、バウンディングボックスという、Mesh全体を覆う直方体の情報を再計算するメソッドです。

最後に (5) で実際の描画処理を行っています。
描画は Graphics.DrawMesh() にメッシュやマテリアルの情報を渡すことで行います。
他にも位置や角度などを渡していますが、ここは適宜設定してください。

まとめ

第一回では、以下のことを行いました。

  • 頂点座標
  • 頂点インデックス
  • 法線
  • Unityでの描画方法

描画したのはシンプルな三角形ではありますが、3Dの世界は全て今回描画したようなシンプルな図形で構成されています。

次回はそのことを体感できるように、少し複雑な立体を描画してみようと思います。

バックナンバー