본문 바로가기
개발/Unity

유니티 - 절차적 메시로 정다각기둥, 원기둥 그리기 (Make Regular Prism, Cylinder with Procedural Mesh)

by 피로물든딸기 2022. 11. 6.
반응형

Unity 전체 링크

 

절차적 메시로 정다각뿔, 원뿔 만들기를 확장하여 정다각기둥, 원기둥을 만들어보자.

 

높이를 설정할 height 변수를 public으로 선언한다.

여기서도 높이의 절반이 오브젝트의 기준점이 된다.

public float height = 1.0f;

 

vertices는 정다각형이 하나 더 추가되므로 polygon + 1개가 추가되고,

정다각형 사이의 사각형이 polygon 만큼 추가되므로 정점의 개수 4를 곱한 값을 더한다.

vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];

 

위와 마찬가지로 triangles는 아래와 같이 크기가 변한다.

triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];

 

첫번째 vertices와 triangles는 밑면을 만든다.

triangles의 방향을 반시계가 되도록 변경하였다.

그리고 height가 y에 들어가도록 좌표를 수정하였다.

    /* -------------------- 밑면 -------------------- */

    vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];

    vertices[0] = new Vector3(0, -height / 2.0f, 0) + offset;
    for (int i = 1; i <= polygon; i++)
    {
        float angle = -i * (Mathf.PI * 2.0f) / polygon;

        vertices[i]
            = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
    }

    triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];
    for (int i = 0; i < polygon - 1; i++)
    {
        triangles[i * 3] = 0;
        triangles[i * 3 + 1] = i + 2;
        triangles[i * 3 + 2] = i + 1;
    }

    triangles[3 * polygon - 3] = 0;
    triangles[3 * polygon - 2] = 1;
    triangles[3 * polygon - 1] = polygon;

 

윗면은 아래와 같다. 

triangles의 방향이 밑면과 다르다는 것을 참고하자.

    /* -------------------- 윗면 -------------------- */

    int vIdx = polygon + 1;
    vertices[vIdx++] = new Vector3(0, height / 2.0f, 0) + offset;
    for (int i = 1; i <= polygon; i++)
    {
        float angle = -i * (Mathf.PI * 2.0f) / polygon;

        vertices[vIdx++]
            = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
    }

    int tIdx = 3 * polygon;
    for (int i = 0; i < polygon - 1; i++)
    {
        triangles[tIdx++] = polygon + 1;
        triangles[tIdx++] = i + 1 + polygon + 1;
        triangles[tIdx++] = i + 2 + polygon + 1;
    }

    triangles[tIdx++] = polygon + 1;
    triangles[tIdx++] = polygon + polygon + 1;
    triangles[tIdx++] = 1 + polygon + 1;

 

옆면은 vIdx를 추가하면서 triangles을 같이 추가하는 것이 편하다.

vertices는 위의 코드를 지나가면 2 * N + 2개의 원소를 가진다.

마찬가지로 triangles는 6 * N개를 가진다.

 

angle과 nextAngle을 기준으로 옆면의 사각형을 하나씩 그려간다.

   /* -------------------- 옆면 -------------------- */

    vIdx = 2 * polygon + 2;
    tIdx = 6 * polygon;
    for (int i = 1; i <= polygon - 1; i++)
    {
        float angle = -i * (Mathf.PI * 2.0f) / polygon;
        float nextAngle = -(i + 1) * (Mathf.PI * 2.0f) / polygon;
        vertices[vIdx]
            = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
        vertices[vIdx + 1]
            = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
        vertices[vIdx + 2]
            = new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
        vertices[vIdx + 3]
            = new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;

        triangles[tIdx++] = vIdx;
        triangles[tIdx++] = vIdx + 2;
        triangles[tIdx++] = vIdx + 1;

        triangles[tIdx++] = vIdx + 2;
        triangles[tIdx++] = vIdx + 3;
        triangles[tIdx++] = vIdx + 1;

        vIdx += 4;
    }

    {
        float angle = -polygon * (Mathf.PI * 2.0f) / polygon;
        float nextAngle = -(0 + 1) * (Mathf.PI * 2.0f) / polygon;
        vertices[vIdx]
            = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
        vertices[vIdx + 1]
            = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
        vertices[vIdx + 2]
            = new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
        vertices[vIdx + 3]
            = new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;

        triangles[tIdx++] = vIdx;
        triangles[tIdx++] = vIdx + 2;
        triangles[tIdx++] = vIdx + 1;

        triangles[tIdx++] = vIdx + 2;
        triangles[tIdx++] = vIdx + 3;
        triangles[tIdx++] = vIdx + 1;
    }

 

게임을 실행하면 정다각기둥을 만들 수 있다.

 

아래는 콜라이더만 남겨서 mesh가 그려진 모습이다.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegularPrism : MonoBehaviour
{
    public int polygon = 3;
    public float size = 1.0f;
    public float height = 1.0f;
    public Vector3 offset = new Vector3(0, 0, 0);

    Mesh mesh;
    Vector3[] vertices;
    int[] triangles;

    void OnValidate()
    {
        if (mesh == null) return;

        if (size > 0 || offset.magnitude > 0 || polygon >= 3 || height > 0)
        {
            setMeshData(size, polygon);
            createProceduralMesh();
        }
    }

    void Start()
    {
        mesh = GetComponent<MeshFilter>().mesh;

        setMeshData(size, polygon);
        createProceduralMesh();
    }

    void setMeshData(float size, int polygon)
    {
        /* -------------------- 밑면 -------------------- */

        vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];

        vertices[0] = new Vector3(0, -height / 2.0f, 0) + offset;
        for (int i = 1; i <= polygon; i++)
        {
            float angle = -i * (Mathf.PI * 2.0f) / polygon;

            vertices[i]
                = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
        }

        triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];
        for (int i = 0; i < polygon - 1; i++)
        {
            triangles[i * 3] = 0;
            triangles[i * 3 + 1] = i + 2;
            triangles[i * 3 + 2] = i + 1;
        }

        triangles[3 * polygon - 3] = 0;
        triangles[3 * polygon - 2] = 1;
        triangles[3 * polygon - 1] = polygon;

        /* -------------------- 윗면 -------------------- */

        int vIdx = polygon + 1;
        vertices[vIdx++] = new Vector3(0, height / 2.0f, 0) + offset;
        for (int i = 1; i <= polygon; i++)
        {
            float angle = -i * (Mathf.PI * 2.0f) / polygon;

            vertices[vIdx++]
                = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
        }

        int tIdx = 3 * polygon;
        for (int i = 0; i < polygon - 1; i++)
        {
            triangles[tIdx++] = polygon + 1;
            triangles[tIdx++] = i + 1 + polygon + 1;
            triangles[tIdx++] = i + 2 + polygon + 1;
        }

        triangles[tIdx++] = polygon + 1;
        triangles[tIdx++] = polygon + polygon + 1;
        triangles[tIdx++] = 1 + polygon + 1;

        /* -------------------- 옆면 -------------------- */

        vIdx = 2 * polygon + 2;
        tIdx = 6 * polygon;
        for (int i = 1; i <= polygon - 1; i++)
        {
            float angle = -i * (Mathf.PI * 2.0f) / polygon;
            float nextAngle = -(i + 1) * (Mathf.PI * 2.0f) / polygon;
            vertices[vIdx]
                = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
            vertices[vIdx + 1]
                = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
            vertices[vIdx + 2]
                = new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
            vertices[vIdx + 3]
                = new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;

            triangles[tIdx++] = vIdx;
            triangles[tIdx++] = vIdx + 2;
            triangles[tIdx++] = vIdx + 1;

            triangles[tIdx++] = vIdx + 2;
            triangles[tIdx++] = vIdx + 3;
            triangles[tIdx++] = vIdx + 1;

            vIdx += 4;
        }

        {
            float angle = -polygon * (Mathf.PI * 2.0f) / polygon;
            float nextAngle = -(0 + 1) * (Mathf.PI * 2.0f) / polygon;
            vertices[vIdx]
                = new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
            vertices[vIdx + 1]
                = new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
            vertices[vIdx + 2]
                = new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
            vertices[vIdx + 3]
                = new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;

            triangles[tIdx++] = vIdx;
            triangles[tIdx++] = vIdx + 2;
            triangles[tIdx++] = vIdx + 1;

            triangles[tIdx++] = vIdx + 2;
            triangles[tIdx++] = vIdx + 3;
            triangles[tIdx++] = vIdx + 1;
        }
    }

    void createProceduralMesh()
    {
        mesh.Clear();
        mesh.vertices = vertices;
        mesh.triangles = triangles;
        mesh.RecalculateNormals();

        Destroy(this.GetComponent<MeshCollider>());
        this.gameObject.AddComponent<MeshCollider>();
    }
}

 

참고로 polygon = 20, size(반지름) = 0.5, height = 2로 설정하면 유니티의 실린더가 된다.

 

유니티의 실린더의 경우 Capsule Collider를 쓰기 때문에 Mesh Renderer를 끄면 차이를 볼 수 있다.

 

참고

- 위의 경우 polygon이 많을 때 불필요한 정점이 상대적으로 많아진다. 

- 따라서 정점을 필요한 만큼만 만들고 triangles로 배치한다.

 

Unity Plus:

 

Easy 2D, 3D, VR, & AR software for cross-platform development of games and mobile apps. - Unity Store

Have a 2D, 3D, VR, or AR project that needs cross-platform functionality? We can help. Take a look at the easy-to-use Unity Plus real-time dev platform!

store.unity.com

 

Unity Pro:

 

Unity Pro

The complete solutions for professionals to create and operate.

unity.com

 

Unity 프리미엄 학습:

 

Unity Learn

Advance your Unity skills with live sessions and over 750 hours of on-demand learning content designed for creators at every skill level.

unity.com

반응형

댓글