posted by REDFORCE 2017. 6. 9. 22:20

이번 글에서는 경량 패턴에 대해서 다루도록 하겠습니다.


3D 게임에서 숲을 구현하고자 할 때, 수 천 그루가 넘는 나무마다 각각 수천 폴리곤의 형태로 표현을 해야합니다. 이 때 메모리가 충분하다하여도 숲을 그리기 위해 전체 데이터를 CPU에서 GPU로 버스를 통해 전달해야합니다. 이때 들어가는 비용은 매우 적지않을거라는 것을 쉽게 추측할 수 있습니다.


이때 나무마다 필요한 데이터들은 일반적으로 다음과 같습니다.


 - 줄기, 가지, 잎의 형태를 나타내는 폴리곤 메시

 - 나무 겁질과 잎사귀 텍스쳐

 - 숲에서의 위치와 방향

 - 각각의 나무가 다르게 보이도록 크기와 음영같은 값을 조절할 수 있는 매게변수


위 항목들을 코드로 나열해보면




대충 이런 내용들이 필요할 것이라 볼 수 있습니다.


관련 이미지

(결과적으로 작은 상자에 들어있는 값들은 모든 나무가 동일하다)


위에서 보듯이 나열되는 항목들의 데이터들이 적지 않은데다가, 메시와 텍스쳐는 크기도 큽니다.

해서  이렇게 많은 객체들(나무)이 있는 숲 전체는 1Frame에 GPU로 모두 전달하기에는 양이 너무 많습니다.


이런 문제에 대해서 다행히, 검증된 해결법으로 아래와 같이 시도하면 됩니다.


핵심은 숲에 나무가 수천그루가 있다고 해도 대부분 비슷해보인다는 점 입니다.

따라서, 모든 나무를 같은 메시와 텍스쳐로 표현할 수 있을거라 가정하고 나무 객체에 들어가는 데이터는 인스턴스별로 크게 다르지 않을 거라는 예상이 보입니다.


이런 가정하에 먼저 객체를 반으로 쪼개어 명시적으로 모델링을 데이터를 나눌 수 있습니다.


모든 나무가 같이 사용하는 데이터를 뽑아내 새로운 클래스에 모아봅니다.



이렇게하면 게임내에서 같은 메시와 텍스쳐를 여러번 메모리에 올릴 필요가 없기 때문에 TreeModel 객체는 하나만 존재하게 됩니다. 이제 각 나무 인스턴스는 공유 객체인 TreeModel을 참조하기만 하면 됩니다.


Tree 클래스에는 인스턴스별로 다르게 표기되어야 하는 데이터들만 아래와 같이 남겨둡니다.



위 형태를 그림으로 표현해보면 아래 그림과 같습니다.




주메모리에 객체를 저장하기 위해서라면 이 정도면 충분하지만, 렌더링을 하는 것은 또 별게의 이야기가 됩니다. 예를들어서 수천개의 인스턴스를 렌더링하기 위해 인스턴스의 Render()를 Call하는 것은 많은 오버헤드를 발생시키게 됩니다.


이런 오버헤드를 막기 위해 요즘나오는 그래픽 카드나 API에서는 여러번 렌더링 되어야하는 인스턴스의 공유 데이터를 받아서 50번 Render()를 Call()해야 할 것을 1번에 전체를 렌더링해주는 방법을 지원해주고 있습니다.


이를 인스턴스 렌더링(instanced rendering) 이라 부릅니다.


이 방법을 이용하면 그리기(draw) 함수를 한 번만 호출하여 전체 숲을 다 그릴 수 있습니다.

(자세한 방법은 인스턴스 렌더링에 대해서 알아보시면 많은 자료들이 나옵니다)