본문 바로가기
개발/Unity

유니티 - 절차적 메시로 정다각뿔, 원뿔 그리기 개선 (Make Regular Pyramids, Cone with Procedural Mesh 2)

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

Unity 전체 링크

 

절차적 메시로 정다각뿔, 원뿔 그리기에서 정점의 개수를 줄여보자.

 

ProceduralRegularPyramidsUpgrade.cs를 추가하고 ProceduralRegularPyramids.cs를 복사한다.

뿔의 옆면 생성 코드를 개선하면 된다.

 

윗부분의 꼭지점을 먼저 만들고 다각형의 정점을 다시 배치한다.

그리고 해당 정점 2 * N개만 triangles에 배치한다. (이전 코드는 4 * N개였다.)

 

따라서 vertices크기가 4N + 1 개에서 2N + 2개로 줄어든다.

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

 

뿔을 만드는 꼭지점을 먼저 vertices에 추가하고 한번 더 정다각형의 꼭지점을 vertices에 넣는다.

그리고 삼각형을 순서대로 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) + i + 1;
        triangles[tIdx++] = (polygon + 1) + i + 2;
        triangles[tIdx++] = (polygon + 1);
    }

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

 

개선된 코드로 게임을 실행해보자. (왼쪽이 Upgrade ver)

N이 작은 경우 정점이 부족하여 라이팅 연산이 제대로 되지 않는다.

wireframe 추가

 

하지만 N이 적절히 크다면 정점이 정보를 잘 공유하여 라이팅 연산을 잘 하게 된다.

즉, N이 클수록 옆면이 연속으로 붙어있게 되므로 정점을 줄이는 것이 성능 향상에 도움이 된다.

그리고 정점이 적은 쪽이 더 부드럽다.

wireframe 추가

 

이전 정다각뿔과 정점을 줄인 정다각뿔을 비교해보자.

N이 클수록 옆면이 연속으로 붙어있게 되므로 정점을 줄이는 것이 성능 향상에 도움이 된다.

 

전체 코드는 다음과 같다.

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

[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegularPyramidsUpgrade : 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;

    public void makePolygon(int polygon)
    {
        setMeshData(size, polygon);
        createProceduralMesh();
    }

    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)];

        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];
        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) + i + 1;
            triangles[tIdx++] = (polygon + 1) + i + 2;
            triangles[tIdx++] = (polygon + 1);
        }

        triangles[tIdx++] = (polygon + 1) + polygon;
        triangles[tIdx++] = (polygon + 1) + 1;
        triangles[tIdx++] = (polygon + 1);
    }

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

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

 

참고 - PolygonController.cs를 빈 오브젝트에 추가하여 동시에 N 값을 변경하였다.

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

public class PolygonController : MonoBehaviour
{
    public GameObject pol1, pol2;
    public int polygon;

    ProceduralRegularPrism prp;
    ProceduralRegularPrismUpgrade prpu;

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

        if (polygon >= 3)
        {
            prp.makePolygon(polygon);
            prpu.makePolygon(polygon);
        }
    }

    void Start()
    {
        prp = pol1.GetComponent<ProceduralRegularPrism>();
        prpu = pol2.GetComponent<ProceduralRegularPrismUpgrade>();
    }
}

 

각 스크립트에는 아래 함수를 추가하였다.

    public void makePolygon(int polygon)
    {
        setMeshData(size, polygon);
        createProceduralMesh();
    }

 

위의 실행결과는 아래의 unitypackage에서 확인 가능하다.

CompareProceduralPyramids.unitypackage
0.00MB

 

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

반응형

댓글