Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Unityでプロシージャルモデリング (直方体)

More than 1 year has passed since last update.

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

今回は直方体を作ってみたいと思います。

box.PNG

Box.cs
using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class Box : MonoBehaviour
{
    [SerializeField] private Vector3 size = new Vector3(5, 4, 3);
    [SerializeField]private Vector3Int segment = new Vector3Int(5, 4, 3);

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

        Vector3Int extendedSeg = segment + Vector3Int.one;
        Vector3[] vertices = new Vector3[2 * ((extendedSeg.x * extendedSeg.y) + (extendedSeg.y * extendedSeg.z) + (extendedSeg.z * extendedSeg.x))];
        int[] triangles = new int[(segment.x * segment.y + segment.y * segment.z + segment.z * segment.x) * 2 * 2 * 3];

        int vi = 0;
        int ti = 0;
        MakeNearPlane(vertices, triangles, ref vi, ref ti);
        MakeFarPlane(vertices, triangles, ref vi, ref ti);
        MakeRightPlane(vertices, triangles, ref vi, ref ti);
        MakeLeftPlane(vertices, triangles, ref vi, ref ti);
        MakeUpPlane(vertices, triangles, ref vi, ref ti);
        MakeDownPlane(vertices, triangles, ref vi, ref ti);

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

    private void MakeNearPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;
        for (int y = 0; y < segment.y + 1; y++)
        {
            for (int x = 0; x < segment.x + 1; x++)
            {
                vertices[vi + vj++] = new Vector3(sizeStep.x * x - halfSize.x, sizeStep.y * y - halfSize.y, -halfSize.z);
            }
        }
        for (int y = 0; y < segment.y; y++)
        {
            for (int x = 0; x < segment.x; x++)
            {
                int v00 = vi + x + y * (segment.x + 1);
                int v10 = vi + (x + 1) + y * (segment.x + 1);
                int v01 = vi + x + (y + 1) * (segment.x + 1);
                int v11 = vi + (x + 1) + (y + 1) * (segment.x + 1);
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }
        vi += vj;
    }

    private void MakeFarPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;
        for (int y = 0; y < segment.y + 1; y++)
        {
            for (int x = 0; x < segment.x + 1; x++)
            {
                vertices[vi + vj++] = new Vector3(sizeStep.x * x - halfSize.x, sizeStep.y * y - halfSize.y, halfSize.z);
            }
        }
        for (int y = 0; y < segment.y; y++)
        {
            for (int x = 0; x < segment.x; x++)
            {
                int v00 = vi + x + y * (segment.x + 1);
                int v10 = vi + (x + 1) + y * (segment.x + 1);
                int v01 = vi + x + (y + 1) * (segment.x + 1);
                int v11 = vi + (x + 1) + (y + 1) * (segment.x + 1);
                ti = MakeQuad(triangles, ti, v00, v01, v10, v11);
            }
        }
        vi += vj;
    }

    private void MakeRightPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;
        for (int y = 0; y < segment.y + 1; y++)
        {
            for (int z = 0; z < segment.z + 1; z++)
            {
                vertices[vi + vj++] = new Vector3(halfSize.x, sizeStep.y * y - halfSize.y, sizeStep.z * z - halfSize.z);
            }
        }
        for (int y = 0; y < segment.y; y++)
        {
            for (int z = 0; z < segment.z; z++)
            {
                int v00 = vi + z + y * (segment.z + 1);
                int v10 = vi + (z + 1) + y * (segment.z + 1);
                int v01 = vi + z + (y + 1) * (segment.z + 1);
                int v11 = vi + (z + 1) + (y + 1) * (segment.z + 1);
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }
        vi += vj;
    }

    private void MakeLeftPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;
        for (int y = 0; y < segment.y + 1; y++)
        {
            for (int z = 0; z < segment.z + 1; z++)
            {
                vertices[vi + vj++] = new Vector3(-halfSize.x, sizeStep.y * y - halfSize.y, sizeStep.z * z - halfSize.z);
            }
        }
        for (int y = 0; y < segment.y; y++)
        {
            for (int z = 0; z < segment.z; z++)
            {
                int v00 = vi + z + y * (segment.z + 1);
                int v10 = vi + (z + 1) + y * (segment.z + 1);
                int v01 = vi + z + (y + 1) * (segment.z + 1);
                int v11 = vi + (z + 1) + (y + 1) * (segment.z + 1);
                ti = MakeQuad(triangles, ti, v00, v01, v10, v11);
            }
        }
        vi += vj;
    }

    private void MakeUpPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;

        for (int z = 0; z < segment.z + 1; z++)
        {
            for (int x = 0; x < segment.x + 1; x++)
            {
                vertices[vi + vj++] = new Vector3(sizeStep.x * x - halfSize.x, halfSize.y, sizeStep.z * z - halfSize.z);
            }
        }

        for (int z = 0; z < segment.z; z++)
        {
            for (int x = 0; x < segment.x; x++)
            {
                int v00 = vi + x + z * (segment.x + 1);
                int v10 = vi + (x + 1) + z * (segment.x + 1);
                int v01 = vi + x + (z + 1) * (segment.x + 1);
                int v11 = vi + (x + 1) + (z + 1) * (segment.x + 1);
                ti = MakeQuad(triangles, ti, v00, v10, v01, v11);
            }
        }
        vi += vj;
    }

    private void MakeDownPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
    {
        Vector3 halfSize = size * 0.5f;
        Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

        int vj = 0;

        for (int z = 0; z < segment.z + 1; z++)
        {
            for (int x = 0; x < segment.x + 1; x++)
            {
                vertices[vi + vj++] = new Vector3(sizeStep.x * x - halfSize.x, -halfSize.y, sizeStep.z * z - halfSize.z);
            }
        }

        for (int z = 0; z < segment.z; z++)
        {
            for (int x = 0; x < segment.x; x++)
            {
                int v00 = vi + x + z * (segment.x + 1);
                int v10 = vi + (x + 1) + z * (segment.x + 1);
                int v01 = vi + x + (z + 1) * (segment.x + 1);
                int v11 = vi + (x + 1) + (z + 1) * (segment.x + 1);
                ti = MakeQuad(triangles, ti, v00, v01, v10, v11);
            }
        }
        vi += vj;
    }

    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;
    }
}

生成されたメッシュは以下のようになります。

box_02.PNG

ソースコードの分量は多いですが、直方体を構成する6つの平面を以下の各メソッドでそれぞれ生成しているだけで、各メソッドの処理内容はほとんど同じです。平面の生成方法については以前の記事を参考にしてください。

MakeNearPlane(vertices, triangles, ref vi, ref ti);
MakeFarPlane(vertices, triangles, ref vi, ref ti);
MakeRightPlane(vertices, triangles, ref vi, ref ti);
MakeLeftPlane(vertices, triangles, ref vi, ref ti);
MakeUpPlane(vertices, triangles, ref vi, ref ti);
MakeDownPlane(vertices, triangles, ref vi, ref ti);

注意しなければならないのは三角形の定義する順番です。例えばMakeNearPlaneMakeFarPlaneはXY平面に対してPlaneを定義するという点では同じですが、NearPlaneを構成する三角形はz軸を+方向に見たときに表側になるようにしなければなりませんが、FarPlaneではz軸を-方向に見たときに表側になるようにしなければなりません。このために、MakeFarPlaneではv01v10を入れ替えてMakeQuadに渡しています。

private void MakeFarPlane(Vector3[] vertices, int[] triangles, ref int vi, ref int ti)
{
    Vector3 halfSize = size * 0.5f;
    Vector3 sizeStep = new Vector3(size.x / segment.x, size.y / segment.y, size.z / segment.z);

    int vj = 0;
    for (int y = 0; y < segment.y + 1; y++)
    {
        for (int x = 0; x < segment.x + 1; x++)
        {
            vertices[vi + vj++] = new Vector3(sizeStep.x * x - halfSize.x, sizeStep.y * y - halfSize.y, halfSize.z); // <= zの値がMakeNearPlaneと異なる
        }
    }
    for (int y = 0; y < segment.y; y++)
    {
        for (int x = 0; x < segment.x; x++)
        {
            int v00 = vi + x + y * (segment.x + 1);
            int v10 = vi + (x + 1) + y * (segment.x + 1);
            int v01 = vi + x + (y + 1) * (segment.x + 1);
            int v11 = vi + (x + 1) + (y + 1) * (segment.x + 1);
            ti = MakeQuad(triangles, ti, v00, v01, v10, v11); // <= v01とv10の渡し方がMakeNearPlaneと異なる
        }
    }
    vi += vj;
}

頂点数は各平面の頂点の和、三角形は各平面の三角形の和になります。直方体の辺では同じ位置に複数の頂点が存在することになりますが、法線を別々に持つ必要があるので気にする必要はありません。

aa_debdeb
Engineer, Creative Coder。Dentsu Craft Tokyo所属。
https://twitter.com/aa_debdeb
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away