고갱

[스타듀 밸리] 3D 게임 개발 #1, Perlin Noise를 이용한 지형 생성 본문

게임 개발/Unity

[스타듀 밸리] 3D 게임 개발 #1, Perlin Noise를 이용한 지형 생성

주인장 고갱 2025. 1. 7. 04:13

본가에 오고나서는 원격으로 작업을 하려고 했었는데, 사용하던 서버 대부분이 초기화하고 세팅 과정 중에 있고,

개인 서버에는 GPU가 없어서 작업도 못하고, 그래서 심심풀이로 게임 개발이나 틈틈히 하고자 한다.

 

이 게임 제작은 각 잡고 진지하게 하는 건 아니고, 취미 겸 심심풀이로 하는 것이라 자주 올라오진 않을 것이다.

 

 

🤔 그래서 어떤 게임을 만들까?

그래서 무슨 게임을 만드냐 하면 요새 할 게 없어서 틈틈히 하고있는 Stardew Valley 게임을 3D로 만들어보고자 한다.

물론 차별점은 당연히 어느 정도 있어야한다고 생각하므로 조금의 독특한 방식은 만들어보고자 한다.

그 차별점 중 하나가 바로 랜덤한 지형이다.

 

Stardew Valley 에서는 집(농장)의 지형이 정해진 몇 가지의 타입 중 하나로 골라야하지만, 내가 만들어볼 게임에서는 Perlin Noise 를 이용하여 랜덤한 지형이 생성되도록 할 것이다.

 

주의할 점은 Perlin Noise 는 Random만 사용한 것과 엄연히 다르다는 점이다.

Perlin Noise는 불규칙적인 무작위 난수를 생성하는 순수 Random 과 다르게 연속적인 난수 값을 생성하는 알고리즘이다.

아래 사진과 같이 Perlin Noise 는 연속적인 흐름을 나타내는 반면에 순수 Random 은 연속적인 특징이 없이 말 그대로 무작위의 난수를 생성함을 볼 수 있다.

각각 Perlin Noise 값과 Random 값을 그래프로 나타낸 모습 사진 출처: KhanAcademy
Unity 공식 문서의 Perlin Noise
완성된 모습

 

이건 예시 형태이긴 한데, 아무튼 Perlin Noise 를 이용하여 이렇게 지형을 만들수 있다.

아래 사진과 같이 마인크래프트처럼 높낮이가 다양한 지형도 이를 응용한 것이다.

 

2년 전에 만들었던 개인 작품, Graphics.DrawMeshInstancedIndirect 함수를 이용하여 그려줘서 성능을 끌어올렸다.

(GameObject를 이용하지 않으므로 많은 갯수의 동일한 메시를 그릴 때 유리함)

근데 지금은 Obsolute 함수로 뜬다. (애초에 저때에도 알고리즘 및 LowLevel 공부 목적이긴 했다.)

2년 전에 만들었던 내 개인 작품

 

아무튼 위 2개의 사진은 원리적으로는 거의 동일하다.

단지 아래는 조금 더 다양한 알고리즘이 쓰였고 위는 기본적인 Perlin Noise만 사용되었다는 차이가 있다.

그러면 어떻게 아래는 지형의 높낮이가 있을 수 있고, 위는 지형의 높낮이가 없을까?

 

간단하게 생각해보자.

Perlin Noise 는 무작위의 난수를 반환한다.

이 난수를 y값으로 이용하지 않고 조건 검사식을 통해 실수 범위에 따라 위 첫 번째 사진처럼 잔디인지, 모래인지, 물인지를 설정할 수 있고, 이 난수에 임의의 정수를 (증폭값, Scale) 곱해주면 위 두 번째 사진과 같이 높낮이를 만들어낼 수도 있다.

 

유니티에서는 이를 간편하게 이용할 수 있게 Mathf.PerlinNoise(float, float) 라는 함수를 제공한다.

 

아주 오래전 이 함수를 처음 사용할 때에는 인자로 x, z값을 그대로 넣어준 적이 있었는데 언뜻보면 그렇게 보일만 하다.

하지만 이는 옳지 않은 방법이다.

 

❓  어떤 문제점?

1. Perlin Noise의 원리와 인자

 

 

왜 옳지 않은지는 Perlin Noise의 원리로 올라가면 쉽게 알 수 있다. (본 게시물에서는 2차원만 다룬다.)

이해를 돕기 위한 사진 출처: Structure-driven phase transitions in paracrystalline topological insulators 논문

 

우선 기본적으로 2차원의 그리드를 임의로 생성한다.

이후 위 그림의 a처럼 그리드를 구분해주는 꼭짓점을 기준으로 꼭짓점마다 랜덤한 기울기 벡터를 부여해준다.

 

이후 그림의 c와 같이 그리드 안에 뿌려진 점과 4개의 꼭짓점간의 거리 벡터를 구해주고 두 벡터를 내적하여 영향력을 구해주는 과정을 거친다.

 

여기까지 했을 때에는 불연속적인 값이 나오므로 e와 f의 보간 절차를 거침으로써 Perlin Noise 값을 구하게 된다.

 

 

 

물론 위 모든 원리를 알고 쓸 필요는 없다.

알아두면 당연히 좋겠지만, 우리에게 필요한건 PerlinNoise 함수에 x, z값을 그대로 넣어주어서는 안되는 이유이다.

 

사진을 보면 알겠지만 그리드는 0 과 1 사이의 값을 가지고 있다. 그렇기 때문에 각 그리드의 네 꼭짓점은 당연하게도 Perlin Noise 값이 적용되지 않는 지점이고, 이는 다시 말해 모든 정수는 Perlin Noise 값이 적용되지 않는다는 것이다.

초록선이 각 꼭짓점을 정확히 지나는 모습 출처: Wikipedia, Perlin Noise

 

모두 동일한 값이 나옴

 

 

그렇다면 이를 해결하는 방법은 무엇일까?

여러가지 해결 방법이 있겠지만 보편적으로는 스케일링이 있다.

x와 z값에 작은 실수를 곱해주는 것이다.

 

그렇게 된다면 결과가 어떻게 되는지 보자.

0에는 그냥 곱해봤다. 무시해도 좋다.
모두 다른 값이 나옴

 

이제 모두 다른 값이 잘 나오게 된다.

여기서 응용해서 Perlin Noise 의 패턴을 더욱 촘촘히 하고싶다면 Scale 값을 올려주고, 반대로 덜 촘촘히 하고싶다면 Scale 값을 낮춰주면 되는 것이다.

 

 

문제가 해결된 것 같지만 아쉽게도 그렇지 않다.

Perlin Noise 를 이용한 지형 생성

 

자 평지를 생성해보았다.

좌 우는 각각 100x100 크기로 Perlin Noise 를 생성하였고 z값은 동일하지만 x값은 다르다. (물론 둘 다 정수가 아니다.)

근데 왜 결과가 같을까?

 

2. 정해진 크기의 그리드

 

유니티에서는 256 단위의 주기를 가지는 그리드를 사용한다.

즉 위 사진의 좌측은 입력 좌표값이 0인 그리드이고, 우측은 입력 좌표값이 256인 그리드, 즉 다시말해 입력 좌표값이 0인 그리드와 동일한 그리드를 지칭하는 것이 된다. (256개로 제한되어 있기 때문에)

 

이해가 안간다면 예시 코드를 통해 살펴보자.

 

이 코드의 결과값은 어떨까?

당연하게도 256은 0과 같아지므로 아래 사진과 같이 모두 동일한 결과가 나오게 된다.

모두 동일한 결과가 나오는 모습

 

 

모두 동일하게 나오는 것이다.

 

그러면 해결 방법이 없을까?

그런건 아니다. 위에서 나왔던 높낮이가 존재하던 랜덤 지형을 구현할 때 썻던 것처럼 여러 알고리즘을 혼합하거나 Octaves를 결합해주면 해결된다.

 

하지만 지금 당장 무한 맵을 만들 것도 아니고 그냥 정해진 크기의 랜덤한 지형이 생성되는게 전부라서 사실 마지막에 언급한 문제는 무시해도 된다. (사실 이렇게 써놓고 로직을 바꿀 예정..)

완료!

 

자 이렇게 오늘 지형 구현을 완료해보았다. (바꿀거지만..)

예전에 해봤던 것들이라 구현하는데에는 사실 5분도 걸리지 않았지만, 개념을 다시 복습하고 글로 표현하고자 실험도 해보고 하다보니 몇 시간은 지난 것 같다.

 

오랜만에 유니티를 만져보니까 느낌이 색다르고 재미도 느껴져서 가끔씩 열어볼 것 같다.

 

 

👍 참고 자료

Perlin Noise 1D

 

Khan Academy

 

ko.khanacademy.org

 

Perlin Noise 위키피디아

 

Perlin noise - Wikipedia

From Wikipedia, the free encyclopedia Type of gradient noise in computer graphics Two-dimensional slice through 3D Perlin noise at z = 0 Perlin noise is a type of gradient noise developed by Ken Perlin in 1983. It has many uses, including but not limited t

en.wikipedia.org

 

참고 논문

Regis, V., Velasco, V., Silva Neto, M., & Lewenkopf, C. H. (2023). Structure-driven phase transitions in paracrystalline topological insulators. arXiv preprint arXiv:2312.08779. https://doi.org/10.48550/arXiv.2312.08779

 

Structure-driven phase transitions in paracrystalline topological insulators

We study phase transitions driven by structural disorder in noncrystalline topological insulators. We introduce a procedural generation algorithm, the Perlin noise, typically used in computer graphics, to incorporate disorder to a two-dimensional lattice,

arxiv.org

 

 

 

 

항상 글을 쓸 때마다 느끼는 것이지만 글 쓰는 것은 서투른 것 같다.

내가 전달하고 싶은 바를 글에 제대로 담아내지 못하는 것 같아서 좀 슬프지만,

언젠가는 글로 다 표현할 수 있는 날이 오길 바란다.