Post | Sanctuary
top of page
  • Join our Discord!
  • Join our Kickstarter!

모두를 지배할 절대고리

저는 OzoneX, 생츄어리 프로젝트의 테크니컬 아티스트이자 렌더링 프로그래머입니다. 저는 아티스트들과 프로그래머들 사이를 연결짓는 역할로서, 지난 10년간 게임 개발업계에서 일해왔습니다. 아티스트들은 대개 게임의 기술적인 분야를 잘 이해하지 못하기 때문에, 그 방면에서 그들에게 기술적인 조언을 주고, 프로그래머들을 위해서는 게임의 메커니즘과 애니메이션, 모델, 아트를 연결짓는 것을 도와 디자인 팀이 원했던 것처럼 보이고 또 행동할 수 있게 돕는 식이죠.


생츄어리 이전에, 저는 수프림 커맨더를 위한 맵 에디터를 작업하고 있었습니다. 이 작업은 제게 어떤 식으로 RTS를 위한 렌더링이 이루어져야 하는지, 어떻게 좋은 최적화를 유지하면서도 멋진 비주얼도 챙겨갈 수 있을지에 대해서 많은 깨달음을 주었습니다.

요약하자면, 프로젝트에서 제 역할은 모든 것들이 최소한의 사양만 사용해서 가장 멋지게 보이도록 할 수 있게 보장하는 것이라고 할 수 있겠네요.

문제


RTS에서, 각각의 유닛이 무엇을 수행하고, 무엇을 보고 감지할 수 있는지 나타내는 것은 매우 중요합니다. 이런 정보를 가장 간단하게 나타내는 방법은 사거리를 표시하는 것이죠. 이 값들의 대부분은 단순히 거리에 불과하니, 대개 같은 반경을 가진 고리 형태로 그대로 나타내줄 수 있게 됩니다. 그래서 저희는 이것들을 ‘사거리 고리’ 라고 부릅니다.


이런 단순한 접근이 가지는 문제점은 다양한 정보를 가지고 있으며 모든 유닛의 사거리가 나타나야 하는 대규모의 유닛 그룹을 선택했을 때 명확하게 나타나게 됩니다. 예를 들어서 레이더, 어뢰 발사관과 주포가 달린 함선만 하더라도 벌써 3개의 서로 다른 정보가 있는 셈인데, 이제 200대의 같은 함선을 선택해야 한다고 생각해봅시다. 그걸 전부 다 보여주려면 각각 다른 위치를 가지고 다른 색깔을 가진 600개의 원을 화면에 나타내야 합니다. 붉은 원 100개만 있어도 뭘 알아보기가 힘든 지경인데 말이죠.

붉은 원 100개


게다가, 그렇게 많은 원들을 지도에 다 표시하는 건 사양을 많이 잡아먹습니다. 더 오래된 게임들에서는 CPU에 메쉬를 생성하는 식으로 이런 원들을 그렸는데, 앞서 설명한 것과 같은 상황에서는 이미 게임을 시뮬레이션하기에 벅찬데도 도형을 그리는 데 상당한 양의 처리 능력이 소모되어야만 했죠. 2D 오버레이같은 단순한 것 때문에 플레이어들이 랙이나 속도 저하를 경험할 수는 없는 일입니다.



해결 방법

이런 데이터의 최적화와 가독성을 개선하는 좋은 방법은 같은 타입의 사거리들을 하나의 형태로 묶는 것이었습니다. 수백개의 원을 그리는 대신 하나의 영역을 그려, 200대의 함선이라도 대포, 어뢰, 레이더를 위한 3개의 영역만 필요하면 되도록 하는 식이죠. 대부분의 경우 이 정도로도 플레이에는 지장이 없을 정도의 정보가 들어왔습니다. 보통은 그룹의 사정거리만을 보게 되니까요. 이 방식은 CPU의 부담을 줄일 뿐만 아니라, 보기에도 훨씬 깔끔해집니다.

모든 원들이 하나로 합쳐진 모습


이제 시각적인 문제는 해결했지만, 이걸 어떻게 코드로 작성하죠? 사실 이것이야말로 가장 어려운 부분이자, 이전에도 많은 RTS 게임들이 마주한 고질적인 문제였습니다. 최선의 가장 진보된 해결책을 찾기 위해, 저는 문제를 세부적인 부분으로 쪼개야만 했죠.

거리 영역


먼저, 원을 그리는 최선의 방법은 무엇일까요? 텍스쳐를 구현해서 지형 위에 펼치는 방식은 많은 문제가 발생합니다. 해상도는 크기에 비례해 증가해야 하고 전략적 줌 때문에 모든 것들이 카메라가 어디 있냐에 상관없이 깔끔하게 보여야 하니까요. 제가 찾아낸 가장 좋은 해결방법 중 하나는 거리 영역을 사용하는 것이었습니다. 거리 영역은 각각의 픽셀이 2D 오브젝트의 표면에 얼마나 가까운지에 대한 정보를 저장하는 텍스쳐입니다. 만약 그 오브젝트가 원형일 경우에는 그 경계에 가장 인접한 지점까지의 거리가 되겠죠.

원을 쉐이더로 묘사하자면 대략 이렇습니다:


UV를 사용한 거리 영역의 최초 실험과 가장 가까운 원으로부터의 거리

이것은 픽셀이 원 안에 있을 때는 음숫값을, 원 밖에 있을 때는 양숫값을 주게 될 것입니다. 이제 거리 영역 안에 있는 픽셀들을 블렌드하면 맵 안에 있는 그 픽셀들의 좌표 사이에서 매끄러운 전환이 가능해지니, 더 이상 텍스쳐의 해상도를 위해 데이터에 한정되지 않습니다. 이제 다른 해상도를 위해서 모든 다른 데이터를 삽입할 수 있으니까요. 이 기법은 보통의 경우에는 어느 거리에서 선명한 폰트를 만들거나, 그림자를 생성하기 위한 필드를 만드는 것처럼 더 복잡한 용도로 쓰입니다.


우리는 또한 그런 데이터에 각도와 반경을 추가했고, 이것은 우리가 나중에 고리의 UV(2D 좌표)를 프로젝트 텍스쳐 안으로 재구축할 수 있도록 합니다.

UV 재구축을 통한 거리 영역의 예시


렌더 파이프라인


이제 어느 거리에서도 잘 보이는 원을 만들었으니, 그걸 수백개나 그려야 하는 단계가 남았습니다. 가장 쉬운 방법은 각각의 원의 거리 영역을 그리는 루프를 만들고 가장 가까운 것만을 표시하도록 하는 것이지만, 그것은 우리가 각각의 원을 각각의 텍스쳐 픽셀을 위해 계산해야 한다는 걸 의미합니다. 즉, 512x512 텍스쳐에 원이 600개가 있다면 6천6백만번을 넘게 계산해야 한다는 뜻이죠... 그것도 게임의 매 프레임마다요! 폰트처럼 그런 데이터를 미리 굽는 것도 불가능합니다. 모든 것이 계속 움직이고 바뀌니까요.


오브젝트를 선별해서 오직 제일 위에 있는 것만 표시하는 방법이 있다면... 잠깐, 생각해보니 있네요! 이것이 바로 3D 렌더링 파이프라인이 작동하는 방식입니다. 먼저 시야 안에 무엇이 있는지 확인하고, 깊이 버퍼를 사용해서 무슨 오브젝트가 제일 위에 표시되는지 확인합니다. 그 다음에야 맨 위에 있는 이 오브젝트를 그리게 되는 것이죠. 게임의 3D 환경에서 화면 위에 그리는 것에는 거의 다 적용할 수 있는 최적화된 과정입니다. 다만, 거대한 월드에서 많은 오브젝트를 동시에 그릴 수 있으려면 빨라야 하죠.

DrawInstancing을 사용하는 것은 동시에 많은 오브젝트들을 상대로 이것을 사용하는 데 좋습니다. Instancing은 위치나 색깔에서 약간의 차이가 있지만 생김새는 같은 오브젝트를 GPU로 그리는 기법입니다. 유니티는 그 방면에서 지원이 잘 되어있고, ComputeBuffers 와 DrawMeshInstancedis를 사용해 우리는 편하게 수천 개의 오브젝트를 그려낼 수 있습니다. 유닛, 전술적 아이콘을 비롯해 게임에서 여럿 보이는 것들이라면 무엇이든지 비슷한 방법으로 그릴 수 있죠.


이제 원을 묘사하는 법도, 그것을 수천개씩 그리는 법도 찾았습니다. 마지막 남은 문제는 어떻게 이 원들을 하나로 블렌딩할지, 어떻게 GPU에게 어떤 원이 가장 가까운지 말해줄 수 있는가였습니다. 우선, 우리는 우리만의 블렌딩 알고리즘을 제작하고 싶지는 않았습니다. 각각의 원이 가진 픽셀이 같은 위치에 있는 다른 고리들과 비교를 하게 되기 때문에, 사양을 많이 잡아먹으니까요. 대신 우리는 이미 가진 것을 사용해서 이 문제를 해결했습니다. 각각의 오브젝트에 저장된, 카메라와 표면이 얼마나 떨어져 있는지에 대한 데이터는 일종의 깊이 버퍼입니다. 표면에 가장 가까운 원이 카메라에 가장 가까운 원으로 나타나게만 하면 되었죠. 그런 효과는 원을 원뿔 형태로 묘사함으로서 달성할 수 있었습니다. 원뿔의 바닥이 고리의 반경이고, 높이가 반경과 같도록 하는 식이었죠. 고리가 클수록 그 중심부 또한 카메라에 가깝게 될 것입니다.


최종적인 거리 영역

거리 영역의 3D 원뿔들


한 가지 어려웠던 도전은 하나의 카메라에서 여러 개의 오브젝트를 각각의 원에 맞는 데이터를 가지도록 별개의 영역 텍스쳐로 그려넣는 것이었습니다. 저는 HDRP renderer를 활용한 유니티의 CustomPass 기능을 활용해서 이걸 이루어낼 수 있었죠. 이 기능은 선택된 렌더링 대기열에서 오브젝트를 모아 같은 프레임에 있는 깊이 버퍼와 같은 카메라를 사용해 렌더 텍스쳐로 그려냅니다. 각각의 고리는 서로 다른 대기열에 렌더링되어야 하지만, 이것은 각각의 고리 탑에 다른 소재를 사용함으로서 해낼 수 있었습니다. 그렇게 큰 손실도 아닙니다. 어차피 개별적으로 렌더링된 것들이니까요.


이 기법은 우리에게 굉장히 뛰어나고 효율적으로 사양을 잡아먹지 않고도 수천 개의 고리를 동시에 렌더링할 수 있는 방법을 제공했습니다. 심지어 가장 큰 맵에서 2만2천개의 원을 그려놓고 실험했는데도 눈에 띌 만한 사양 저하가 일어나지 않았죠. 우리는 이것을 전장의 안개와 지형에 난 구멍을 구현하는 데도 사용할 수 있었습니다.

22000 개의 거리 영역과 전장의 안개


25 views0 comments
bottom of page