LoginSignup
3
1

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-07-15

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

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

cylinder.PNG

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

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

cylinder_ex_bottom.png

円筒部分は平面の水平方向の端を繋げるように構成しています。頂点を円周上に配置している点と水平方向の端をつなげるようにしてる点以外は、以前の記事で説明した平面メッシュとほぼ同じになると思います。

cylinder_ex_side.png

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

cylinder_ex_top.png

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1