Unity/그래픽스프로그래밍

Unity PROCEDURAL TERRAIN _ 여러 개 점,면 만들어서 노이즈 맵으로 지형 만들기

minquu 2022. 10. 10. 18:41
반응형

.☝ PROCEDURAL은 절차적인 의미로써 모델링 또는 fx에서 많이 쓰인다.

프로시졀의 장점은 예측이 가능하고, 특정 부분에서 런타임중 접근이 용이하다.

일반적으로 모델러가 만들어준 모델링은 그 과정을 알 수가 어렵기 때문에 항상 결과물만 이용하게 된다.

하지만, 프로시졀 모델링은 그 과정을 알 수 있기 때문에 수정이 용이하다

 

대표적으로 프로시졀 모델링은 후디니에서 많이 사용한다. (노드 기반)

 

-----

 

저번에 이어서 면을 만들어 줄 것인데

이번에는 점을 가지고 있는 플랜한 면을 만들어 줄 것이다.

 

기본적인 오브젝트를 하나 만들어주고,

메시 필터, 메시 렌더러, 만들어준 스크립터를 달아준다.

 

using UnityEngine;

public class MeshGenerate : MonoBehaviour
{
    Mesh mesh;
    Vector3[] vertices;
    int[] triangles;

    [SerializeField]
    public int xSize = 20, zSize = 20;


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

        CreateShape();
        UpdateMesh();
    }

    private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                vertices[i] = new Vector3(x, 0, z);
                i++;
            }
        }
    }

    private void UpdateMesh()
    {
        mesh.Clear();

        mesh.vertices = vertices;
        mesh.triangles = triangles;

        mesh.RecalculateNormals();
    }

    private void OnDrawGizmos()
    {
        if (vertices == null) return;

        for (int i = 0; i < vertices.Length; i++)
        {
            Gizmos.DrawSphere(vertices[i], 0.1f);
        }
    }
}

 

 

먼저 변수쪽을 보면

 

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

    [SerializeField]
    public int xSize = 20, zSize = 20;

mesh 는 만들어줄 면을 뜻하고

Vec3 배열은 만들어줄 점들의 배열

int 배열은 면을 만들어줄 정점의 포인트 지점

 

xSize와 zSize 는 

만들어줄 플랜의 x개수, z 개수를 뜻한다.

 

 

---

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

        CreateShape();
        UpdateMesh();
    }

 

처음에 

새로운 mesh를 만들어주고,

현재 컴포넌트에 있는 메시필터에 만들어준 mesh를 넣어준다.

 

CreateShape 와 UpdateMesh 메서드를 호출 해준다.

 

---

    private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                vertices[i] = new Vector3(x, 0, z);
                i++;
            }
        }
    }

CreateShape를 보면

xSize와 zSize로 정점을 만들어준다.

 

0 지점 때문에 +1 한 개수로 총 정점에 대한 배열 사이즈를 지정해준다.

 

이중배열로 z for를 돌리고, x for를 돌려준다. 

0지점부터 만들어주기 때문에  <= 를 달아줘야한다.

i 는 총 점의 갯수의 인덱스 값임

 

z 인덱스가 0 일때 

vertices[0] = new Vector3(0, 0, 0); 

vertices[1] = new Vector3(1, 0, 0); 

vertices[2] = new Vector3(2, 0, 0); 

..... 쭉쭉 생성이 되고,

x값 만큼 다 돌면

 

z인덱스가 1이 된다

vertices[21] = new Vector3(0, 0, 1); 

vertices[22] = new Vector3(1, 0, 1); 

vertices[23] = new Vector3(2, 0, 1); 

쭉쭉  생성이 된다.

 

----

 

    private void UpdateMesh()
    {
        mesh.Clear();

        mesh.vertices = vertices;
        mesh.triangles = triangles;

        mesh.RecalculateNormals();
    }

    private void OnDrawGizmos()
    {
        if (vertices == null) return;

        for (int i = 0; i < vertices.Length; i++)
        {
            Gizmos.DrawSphere(vertices[i], 0.1f);
        }
    }

 

업데이트  메서드에서는

위에서 만들어준 배열 값을 최신화 시켜주는 역할을한다. 

 

그리고 정점을 보기 위해서

OnDrawGizmos 로 정점 길이와, 값을 가져와서 그려준다.

 

 

-----

플레이 해주면 20x20 정점이 만들어진다.

 

---

 

이제 면을 채워줄 것이다.

 

    private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                vertices[i] = new Vector3(x, 0, z);
                i++;
            }
        }

        triangles = new int[3];
        triangles[0] = 0;
        triangles[1] = xSize + 1;
        triangles[2] = 1;
    }

CreateShape 메서드에 

 

        triangles = new int[3];
        triangles[0] = 0;
        triangles[1] = xSize + 1;
        triangles[2] = 1;

정점에 대한 인트 값을 넣어준다.

점을 그려주는 데, 정점에 대한 인트 값은

반 시계 방향으로 그려준다.

1번 째는 0

2번 째는 총 x 사이즈의 +1 인 -> 4가 되고

3번째는 1이 된다.

 

플레이를 하게되면  정상적으로 삼각형 면 하나가 만들어진다.

 

 

        triangles = new int[6];
        triangles[0] = 0;
        triangles[1] = xSize + 1;
        triangles[2] = 1;
        triangles[3] = 1;
        triangles[4] = xSize + 1;
        triangles[5] = xSize + 2;

나머지 면을 그려주려면 

 

이런 방식이기 때문에

 

        triangles[3] = 1;
        triangles[4] = xSize + 1;
        triangles[5] = xSize + 2;

 

정점을 추가시켜준다. 

 

정상적으로 한개의 면이 만들어진다.

 

이걸 이제 for를 돌리면서 여러개 생성을 해준다.

 

    private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                vertices[i] = new Vector3(x, 0, z);
                i++;
            }
        }

        triangles = new int[xSize * zSize * 6];

        int vert = 0;
        int tris = 0;
        for (int x = 0; x < xSize; x++)
        {
            triangles[tris + 0] = vert + 0;
            triangles[tris + 1] = vert + xSize + 1;
            triangles[tris + 2] = vert + 1;
            triangles[tris + 3] = vert + 1;
            triangles[tris + 4] = vert + xSize + 1;
            triangles[tris + 5] = vert + xSize + 2;

            vert++;
            tris += 6;
        }
    }
        triangles = new int[xSize * zSize * 6];

        int vert = 0;
        int tris = 0;
        for (int x = 0; x < xSize; x++)
        {
            triangles[tris + 0] = vert + 0;
            triangles[tris + 1] = vert + xSize + 1;
            triangles[tris + 2] = vert + 1;
            triangles[tris + 3] = vert + 1;
            triangles[tris + 4] = vert + xSize + 1;
            triangles[tris + 5] = vert + xSize + 2;

            vert++;
            tris += 6;
        }

우선 triangles의 정점 총 갯수는 

xSize와 zSize와 한면을 만들기 위한 6개 정점을 모두 곱한 값임

 

xSize * zSize * 6 를 해줌

 

int vert 와 tris 인덱스를 변수로 만들어주고,

 

먼저 xSize에 대한 for 를 만들어준다.

 

triangles[tris + 0] 에 tris 값으로 인덱서를 해준다.

한 면을 만드는 사이클이 6이기 때문에

for 마지막에서 += 6; 연산을 해주기 때문에

 

triangles에 인덱서는 for를 돌 때마다 새로운 정점을 지정하게 됨.

 

vert는 1씩 증가하여 1씩 증가된 값을 가지게 한다. 

 

한 개 삼각형을 그릴때 이런식으로 작동함

플레이하면 xSize에 대한 면이 만들어진다.

 

 

이제 zSize 값만큼 이중 포문으로 해주면 나머지 면도 추가해준다.

 

    private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                vertices[i] = new Vector3(x, 0, z);
                i++;
            }
        }

        triangles = new int[xSize * zSize * 6];

        int vert = 0;
        int tris = 0;

        for (int z = 0; z < zSize; z++)
        {
            for (int x = 0; x < xSize; x++)
            {
                triangles[tris + 0] = vert + 0;
                triangles[tris + 1] = vert + xSize + 1;
                triangles[tris + 2] = vert + 1;
                triangles[tris + 3] = vert + 1;
                triangles[tris + 4] = vert + xSize + 1;
                triangles[tris + 5] = vert + xSize + 2;

                vert++;
                tris += 6;
            }
            vert++;
        }
    }

 

        triangles = new int[xSize * zSize * 6];

        int vert = 0;
        int tris = 0;

        for (int z = 0; z < zSize; z++)
        {
            for (int x = 0; x < xSize; x++)
            {
                triangles[tris + 0] = vert + 0;
                triangles[tris + 1] = vert + xSize + 1;
                triangles[tris + 2] = vert + 1;
                triangles[tris + 3] = vert + 1;
                triangles[tris + 4] = vert + xSize + 1;
                triangles[tris + 5] = vert + xSize + 2;

                vert++;
                tris += 6;
            }
            vert++;
        }

 

z 이중 포문으로 해주고, 

x 한번 다 돌면

vert++를 해줘야지 다음 위 칸에 대한 vert 계산을 하기 댸문에 

z 포문 안에 vert++;를 해준다 .

 

 

이런 플랜을 만들 수 있음 

 

추가적으로 버텍스의 y 값을 노이즈맵으로 높이 값을 줄 수 있음 

 

 

 private void CreateShape()
    {
        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                float y = Mathf.PerlinNoise(x * 0.3f, z * 0.3f) * 2f;
                vertices[i] = new Vector3(x, y, z);
                i++;
            }
        }

        triangles = new int[xSize * zSize * 6];

        int vert = 0;
        int tris = 0;

        for (int z = 0; z < zSize; z++)
        {
            for (int x = 0; x < xSize; x++)
            {
                triangles[tris + 0] = vert + 0;
                triangles[tris + 1] = vert + xSize + 1;
                triangles[tris + 2] = vert + 1;
                triangles[tris + 3] = vert + 1;
                triangles[tris + 4] = vert + xSize + 1;
                triangles[tris + 5] = vert + xSize + 2;

                vert++;
                tris += 6;
            }
            vert++;
        }
    }

 

 

        vertices = new Vector3[(xSize + 1) * (zSize + 1)];

        for (int i = 0, z = 0; z <= zSize; z++)
        {
            for (int x = 0; x <= xSize; x++)
            {
                float y = Mathf.PerlinNoise(x * 0.3f, z * 0.3f) * 2f;
                vertices[i] = new Vector3(x, y, z);
                i++;
            }
        }

 

버텍스를 만들어주는 for문에서

y 값을  PerlinNoise 값을 사용해준다.

 

[PerlinNoise] 관련

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=daehuck&logNo=220457203383 

 

Perlin Noise 알고리즘

Perlin Noise Perlin Noise는 Ken Perlin에 의해 1983년에 고안된 노이즈 기법입니다. Perlin Nois...

blog.naver.com

 

 

y 값을 PerlinNoise 값을 이용해서 넣어주면

 

이러한 노이즈맵이 만들어진다.

 

------

 

메쉬는 결국 정점이 모여서 면이 만들어 지는 것!

정점을 잘 이용하면 재미있는 것들이 가능 할 듯! 

 

굳 .👍

반응형