Unityでプロシージャルモデリング (円柱)

Unityでメッシュをスクリプトでプロシージャルに生成する方法について何回かに分けて書いていきます。

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


今回は円柱を作ってみます。

![cylinder.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/0d8643f5-005f-f73a-f075-6f32c2b65d67.png)


```csharp:Cylinder.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Cylinder : MonoBehaviour
{
    [SerializeField] private int heightSegments = 3;
    [SerializeField] private int angleSegments = 8;

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

        Vector3[] vertices = new Vector3[(1 + angleSegments) * 2 + (heightSegments + 1) * angleSegments];
        int[] triangles = new int[angleSegments * 3 * 2 + heightSegments * angleSegments * 2 * 3];

        float angleStep = Mathf.PI * 2.0f / angleSegments;
        float heightStep = 2.0f / heightSegments;
        int vi = 0;
        int vj = 0;
        int ti = 0;

        // creates bottom cap
        vertices[vj++] = new Vector3(0, -1, 0);
        for (int ai = 0; ai < angleSegments; ai++)
        {
            float angle = ai * angleStep;
            vertices[vj++] = new Vector3(Mathf.Cos(angle), -1, Mathf.Sin(angle));
        }
        for (int ai = 0; ai < angleSegments; ai++)
        {
            triangles[ti++] = 0;
            triangles[ti++] = ai + 1;
            triangles[ti++] = ai != angleSegments - 1 ? ai + 2 : 1;
        }
        vi += vj;

        // creates tube
        vj = 0;
        for (int hi = 0; hi < heightSegments + 1; hi++)
        {
            float height = -1.0f + hi * heightStep;
            for (int ai = 0; ai < angleSegments; ai++)
            {
                float angle = ai * angleStep;
                vertices[vi + vj++] = new Vector3(Mathf.Cos(angle), height, Mathf.Sin(angle));
            }
        }
        for (int hi = 0; hi < heightSegments; hi++)
        {
            for (int ai = 0; ai < angleSegments; ai++)
            {
                int aj = ai != angleSegments - 1 ? ai + 1 : 0;
                int v00 = ai + hi * angleSegments + vi;
                int v10 = aj + hi * angleSegments + vi;
                int v01 = ai + (hi + 1) * angleSegments + vi;
                int v11 = aj + (hi + 1) * angleSegments + vi;
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }
        vi += vj;

        // creates top cap
        vj = 0;
        for (int ai = 0; ai < angleSegments; ai++)
        {
            float angle = ai * angleStep;
            vertices[vi + vj++] = new Vector3(Mathf.Cos(angle), 1, Mathf.Sin(angle));
        }
        vertices[vi + vj++] = new Vector3(0, 1, 0);
        for (int ai = 0; ai < angleSegments; ai++)
        {
            triangles[ti++] = vertices.Length - 1;
            triangles[ti++] = (ai != angleSegments - 1 ? ai + 1 : 0) + vi;
            triangles[ti++] = ai + vi;
        }

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

    private int MakeQuad(int[] triangles, int ti, int v00, int v10, int v01, int v11)
    {
        triangles[ti] = v00;
        triangles[ti + 1] = triangles[ti + 5] = v01;
        triangles[ti + 2] = triangles[ti + 4] = v10;
        triangles[ti + 3] = v11;
        return ti + 6;
    }
}
```

プログラムでは`heightSegments`で高さ方向の分割数を、`angleSegments`で円の分割数を任意に設定できます。デフォルト値では、以下のような三角形で構成されます。
プログラムでは底面、円筒、上面に分けて、順々に頂点と三角形を定義しています。

![cyllinder_02.PNG](https://qiita-image-store.s3.amazonaws.com/0/61569/9124aa16-d5c2-a319-ab54-e29cd28ba8a7.png)

以下は底面から円柱を見上げたときの画像で、数字は頂点のインデックスで、丸に囲まれた数字は三角形の定義する順番を表しています。底面の頂点と三角形の構成方法については、[以前の記事](https://qiita.com/aa_debdeb/items/d34bd733e0584494bdbd)で説明した正多角形メッシュ作成方法と同じなのでそちらを参考にしてください。

![cylinder_ex_bottom.png](https://qiita-image-store.s3.amazonaws.com/0/61569/45c9bc4b-9a50-9c9b-a9cd-81a002ab3543.png)


円筒部分は平面の水平方向の端を繋げるように構成しています。頂点を円周上に配置している点と水平方向の端をつなげるようにしてる点以外は、[以前の記事](https://qiita.com/aa_debdeb/items/043695f998606876cfb4)で説明した平面メッシュとほぼ同じになると思います。

![cylinder_ex_side.png](https://qiita-image-store.s3.amazonaws.com/0/61569/aaa95d6b-fa59-e8bb-7de2-a4eba533733c.png)

上面は底面と同様の方法で作成していますが、頂点の定義順が異なるのでそれに合わせて三角形の定義順も変更しているので注意してください。次の画像は円柱を上から見下ろしたときの画像です。真ん中の頂点を最後に定義しています。

![cylinder_ex_top.png](https://qiita-image-store.s3.amazonaws.com/0/61569/8301ad3a-8b0d-d70a-b54e-2bc088ebde96.png)