Unityでプロシージャルモデリング (三角形・四角形・正多角形)

Unityでスクリプトを用いてメッシュをプロシージャルに生成する方法について、何回かに渡って書いていきたいと思います。

* 第1回 Unityでプロシージャルモデリング (三角形・四角形・正多角形)
* [第2回 Unityでプロシージャルモデリング \(平面\) \- Qiita](https://qiita.com/aa_debdeb/items/043695f998606876cfb4)
* [第3回 Unityでプロシージャルモデリング \(直方体\) \- Qiita](https://qiita.com/aa_debdeb/items/983c264879084bd40b0c)
* [第4回 Unityでプロシージャルモデリング \(円柱\) \- Qiita](https://qiita.com/aa_debdeb/items/c54b14696363873c640d)
* [第5回 Unityでプロシージャルモデリング \(球\) \- Qiita](https://qiita.com/aa_debdeb/items/cc46409b99301a5e34c0)
* [第6回 Unityでプロシージャルモデリング \(トーラス\) \- Qiita](https://qiita.com/aa_debdeb/items/efc188aa3c03a8acc5b9)

今回は手始めとして、２次元図形のメッシュを生成してみたいと思います。

# 三角形

![triangle.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/96978224-3ee1-97a9-3518-30019350c911.png)


すべてのメッシュは複数の三角形で構成されているように、三角形は最も基本的な図形になります。
以後作成していく図形もすべて、三角形を組み合わせて生成していきます。

以下が三角形メッシュを生成するスクリプトになります。このスクリプトを空のGameObjectにアタッチすると、プレイモード実行時にメッシュが生成されて三角形が表示されます。

```csharp:Triangle.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Triangle : MonoBehaviour {

    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[3];
        int[] triangles = new int[3];
        for (int i = 0; i < 3; i++)
        {
            float angle = i * 120f * Mathf.Deg2Rad;
            vertices[i] = new Vector3(Mathf.Sin(angle), Mathf.Cos(angle), 0);
        }
        triangles = new int[3] { 0, 1, 2 };

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }
}
```

メッシュの生成・表示には`MeshFilter`コンポーネントと`MeshRender`コンポーネントが必要になるので、`RequireComponent`でアタッチするようにしています。

メッシュには頂点の情報(`mesh.vertices`)と、どの頂点で三角形を構築するか(`mesh.triangles`)という情報が必要になります。今回はXY平面上で正三角形になるように頂点を設定しています。

`mesh.triagles`にどの頂点を利用して三角形を作成するかを頂点のインデックスで指定します。今回は0番目・1番目・2番目の頂点で三角形を構築しています。
三角形プリミティブには表・裏があり、頂点が時計回り順に定義されているときに表だと判断されます。基本的に裏側を向いているとCullingされるので注意してください。（例えば、`triangles = new int[3] { 0, 2, 1 }`とした場合、+z軸方向を見るデフォルトのカメラ位置ではメッシュが表示されなくなります。）

![triangle_ex.png](https://qiita-image-store.s3.amazonaws.com/0/61569/d130a7a8-cb9e-f6f1-bfa6-156adf1bf08c.png)


`mesh.RecalculateNormals()`は頂点と三角形の情報をもとに法線を計算しています。頂点と同じように自分で法線の配列を用意して`mesh.normals`に代入しても法線を設定することができます。法線を設定することでライティングが正しく行われるようになります。

もし、テクスチャを利用したい場合には`mesh.uv`にVector2の配列を代入することで、UV座標を設定する必要があります。また、バンプマップを利用したい場合は`mesh.RecalculateTangents()`で接線を計算する必要があります。


# 四角形

四角形です。四角形は三角形よりも取り扱いやすいので、いろいろな形状を作るときに利用できます。

![quad.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/02830c20-a708-fe6b-870b-15afc2548c93.png)


```csharp:Quadrangle.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Quadrangle : MonoBehaviour
{
    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[4];
        int[] triangles = new int[6];
        
        vertices[0] = new Vector3(-0.5f, -0.5f, 0);
        vertices[1] = new Vector3(0.5f, -0.5f, 0);
        vertices[2] = new Vector3(-0.5f, 0.5f, 0);
        vertices[3] = new Vector3(0.5f, 0.5f, 0);

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

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }
}
```

四角形は2つの三角形で構築することができます。2つの三角形を構成する頂点のうち2つは重なっているので頂点は4つだけ必要になります。
`triagnles`配列の先頭3つの値(`0, 2, 1`)は1つ目の三角形を構成する頂点のインデックス、次の3つの値(`3, 1, 2`)は2つ目の三角形を構成する頂点のインデックスになります。このように`triangles`配列の大きさは`メッシュを構成する三角形の数 * 3`になるように設定します。

![quad_ex.png](https://qiita-image-store.s3.amazonaws.com/0/61569/7b130151-fff6-b15f-fff9-77a750e53b16.png)


# 正多角形

![polygon.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/77423ad1-0134-9302-04ba-55d3bafa0651.png)


頂点の数(正N角形のN)を任意に設定できるようにした正多角形です。

```csharp:Polygon.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Polygon : MonoBehaviour
{
    [SerializeField] private int vertexNum = 5;

    private void Awake()
    {
        Mesh mesh = new Mesh();
        GetComponent<MeshFilter>().mesh = mesh;

        Vector3[] vertices = new Vector3[vertexNum + 1];
        int[] triangles = new int[vertexNum * 3];

        vertices[0] = Vector3.zero;
        float angleStep = Mathf.PI * 2.0f / vertexNum;
        for (int i = 0; i < vertexNum; i++)
        {
            vertices[i + 1] = new Vector3(Mathf.Sin(i * angleStep), Mathf.Cos(i * angleStep), 0);
        }
        for (int i = 0; i < vertexNum; i++)
        {
            triangles[i * 3] = 0;
            triangles[i * 3 + 1] = i + 1;
            triangles[i * 3 + 2] = i + 2 != vertexNum + 1 ? i + 2 : 1;
        }

        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();
    }
}
```

真ん中に頂点を一つ置いて、ケーキをカットするように三角形を作っています。

![polygon_ex.png](https://qiita-image-store.s3.amazonaws.com/0/61569/bb72151c-a5d8-35f9-ed73-e2fe088b50cc.png)