https://www.youtube.com/watch?v=heoXRhQv8cs

안녕하십니까, 제 발표에 오신 여러분을 환영하며 참석해주셔서 감사합니다.

제 이름은 Giang이며, 'z' 발음으로 읽습니다. 저는 Studio GoBo의 CoreTech 팀 엔지니어로서, 오늘 여러분께 게임 Lego Horizon Adventures의 Nintendo Switch 버전 테레인(terrain) 제작 과정을 이야기하고자 이 자리에 섰습니다.

오늘은 여러분께서 'story'라는 단어를 들어보셨을 테고, 바로 그 내용에 대해 이야기할 것입니다. 이 자리는 제가 성공담을 늘어놓는 일반적인 발표와는 다를 것입니다.

이러한 '성공으로 이어질 것'이라는 식의 단순한 나열 대신, 본질에 집중합니다. 이는 반복적인 개선 과정을 통해 목표를 달성하는 데 초점을 맞추고 있습니다.

Switch 플랫폼 출시를 위해 특정 문제를 해결하기 위해 수행했던 작업에 대해 설명드립니다.

수차례의 실험적 시도가 실패로 돌아갔으며, 저희의 예상과 다른 결과를 발견했습니다. 부디 저희의 이러한 경험이 향후 유사한 문제에 직면하실 때 도움이 되기를 바랍니다.

지체 없이 LEGO 테레인(terrain) 여정을 시작하도록 하겠습니다.

먼저 닌텐도 스위치로 촬영된 게임 플레이 영상 몇 개를 살펴보도록 하겠습니다.

세상에.

알겠습니다.

이 내용을 보시고 마음에 드셨기를 바랍니다. 저희 Studio Global 개발팀이 만든 결과물입니다.

영국 브라이튼에 기반을 둔 저희 팀은 영국 내외에서 원격으로 근무하는 많은 멤버들로 구성되어 있습니다. 이전에 Legal Horizon Adventures와 함께 여러 타이틀을 공동 개발한 경험이 있습니다. 이번에는 Guerrilla, QLog 및 기타 파트너사와 함께 풀 개발사로서 새로운 도전을 하였습니다.

저는 베트남 하노이 출신입니다. 경력의 전반부에는 일부 모바일 게임의 gameplay programmer로 활동했습니다. 영국으로 이주한 후에는 lower level 분야의 작업을 시작했으며, 이에 즐거움을 느끼기 시작했습니다.

게임 영상에서 보신 것처럼, LEGO Horizon Adventures는 LEGO 브릭으로 완전히 만들어진 세상을 내려다보는 탑다운 뷰를 특징으로 하며, 물 또한 브릭 기반이라는 점을 확인하실 수 있습니다.

실시간으로 영화 같은 수준을 달성하고자 하였습니다. 이를 통해 플레이어가 작고 현실적인 벽돌로 만들어진 세계에 몰입할 수 있기를 바랍니다.

저희가 타겟으로 하는 플랫폼은 PC, PS5, Nintendo Switch이며, Unreal 5.3을 사용합니다.

이제 본격적인 문제 해결 부분으로 들어가기 전에, 'Little Horizon'의 terrain이 어떻게 제작되었는지 살펴보겠습니다.

LEGO 게임의 전형적인 방식에서 벗어나, LEGO 미니피겨를 일반적인 게임 환경에 배치하는 방식으로 새로운 여정을 시작하고자 합니다.

저희의 아트 디렉션은 모든 것이 벽돌 하나하나 쌓아 올리듯이 구축됩니다. 따라서 저희가 본질적으로 추구하는 것은...

브릭(brick) 형태의 horizon terrain에 대한 내용입니다. 처음에는 이와 같이 수작업으로 제작된 독특한 terrain 모델을 사용하고자 하였으나, 제작에 많은 노력이 필요하고 각 모델당 수개월이 소요될 수 있어 신속하게 폐기되었습니다.

또한, 게임플레이 카메라에서 볼 때 노이즈(noise)가 많이 발생합니다.

저희 아티스트들은 모듈식 타일(modular tiles)을 활용하는 방식을 채택했습니다. 이 모듈식 타일들은 크기와 각도에 대한 엄격한 규칙에 따라 맞춰집니다. 아트 디렉션 관점에서 이러한 방식으로 지형을 디자인하게 된 과정은 꽤 긴 이야기가 될 수 있습니다.

저의 동료 James와 Rich가 GTC talk에서 다루었던 내용입니다.

본론으로 돌아와, 지형 타일을 일일이 배치하는 작업은 매우 힘들고 지루한 과정입니다. 이를 해결하기 위해 언리얼 엔진의 다른 레벨 디자인 툴과 함께 사용하는 지형 툴을 개발하였습니다. 이 툴은 페인팅 방식의 인터랙션을 지원합니다. 이를 통해 아티스트와 레벨 디자이너는 각 바이옴에 맞게 디자인된 다양한 타일 타입과 타일 셋으로 지형 패치를 칠할 수 있습니다. 이에 대한 자세한 내용은 추후 설명드리겠습니다.

중요한 점은 이들이 즉각적인 feedback을 얻을 수 있다는 것입니다. 이를 통해 실험과 탐색을 쉽고 빠르게 진행할 수 있습니다.

실제 작동 모습은 다음과 같습니다.

네, 사용자 관점에서 본 테레인 툴(terrain tool)에 대한 설명이었습니다.

더욱 기술적인 관점에서 볼 때, 왼쪽에서 보시는 바와 같이 툴은 각 타일의 코너에 대한 좌표, 높이, 타입을 나타내는 그리드 데이터를 출력합니다. 이 그리드 데이터는 영구적으로 저장되며, terrain tool이 내부적으로 작동하는 기반이 됩니다. 저희는 이 데이터를 terrain patches라는 Actives에 저장합니다. 이 데이터는 특정 타일이 어떤 시각적 형태를 가질지를 결정합니다.

타일 데이터와 타일 셋을 사용하여 최종 테레인 표현을 생성할 수 있습니다. 화면 오른쪽 끝에서 타일 코너가 주석 처리된 최종 테레인 표현을 확인하실 수 있습니다.

타일셋에서 가져온 예시 타일들을 사용하여 지형 패치를 구성하는 방식을 보여드리겠습니다. 타일셋은 기본적으로 각 높이 조합에 어떤 mesh를 사용할지에 대한 설정을 포함하고 있습니다.

중복을 피하기 위해 각 타입별로 고유한 Variance가 존재합니다.

이제 다른 내용들은 치우고, 개념을 설명하기 위해 몇 가지 타일을 살펴보겠습니다. 이 타일은 세 개의 코너가 path이고 하나의 코너가 ground임을 알 수 있습니다. 따라서 이와 같은 타일을 사용해야 합니다. 다음 타일도 마찬가지입니다. 원리는 같지만 메쉬만 다릅니다.

16x16 또는 32x32 크기의 더 큰 타일도 있습니다. 예를 들어, 이 타일은 모서리 중 하나가 다른 모서리보다 높게 위치한 경우입니다.

경사면 메쉬를 선택합니다. 지형의 가장자리를 더 두드러지게 만들기 위해 높이를 낮추는 반대 경우에도 동일한 원리가 적용됩니다.

지형 데이터를 표현과 분리하는 또 다른 이점은 게임 내에서 볼 수 있듯이, 다양한 biomes를 나타내는 몇 가지 tile set을 보유할 수 있다는 점입니다. 이를 통해 디자이너들은 재구성할 수 있습니다.

지형 패치가 즉시 로드됩니다. 이제 타일에 대해 좀 더 자세히 살펴보겠습니다. 다른 모든 게임 내 지오메트리와 마찬가지로 지형 타일은 동일한 메쉬 파이프라인으로 구동됩니다. 이 파이프라인 뒤에는 절차적 Houdini black magic이 숨어 있습니다. 이에 관심이 있으시다면 동료 Simon의 관련 발표를 확인해 보시기를 권합니다. 하지만 저희는,

저희가 신경 쓰는 부분은 특화된 Lego 모델링 소프트웨어 내에서 제작되는 모델로부터 최적화된 mesh를 생성하는 것입니다. 생성된 mesh lot zero는

고성능 플랫폼에서는 Nanite와 Lumen과 같은 최신 기술을 활용합니다. 반면 Switch에서는 Lot 1부터 HeigerLots를 사용하며, draw call을 발행하는 전통적인 렌더링 방식을 적용합니다. 따라서 Heiger

'Lord's geometry'에는 레고 스터드(Lego studs) 상표가 적용되지 않습니다. Switch에서는 스터드를 별도의 pass에서 카메라를 향하는 빌보드(billboards)로 레이 트레이싱(ray tracing)하여 단 한 번의 draw call로 렌더링합니다. 이는 단순히 지오메트리(geometry)를 렌더링하는 방식과는 다릅니다. 이 내용은 제 동료 Ali가 Digital Dragons 강연에서 다루었습니다.

게임의 렌더링(rendering) 측면에 집중하고 있습니다.

이어서, Turian 패치는 recolouring tool을 사용하여 위에 칠할 수도 있습니다. 이 툴은 brush style painting과 bucket style painting을 지원합니다. 또한, 이러한 custom colors는 나중에 재사용할 수 있도록 color presets으로 저장할 수도 있습니다.

Recoloring은 모델 에셋을 복제하지 않고도 시각적 다양성을 증대시키는 기능입니다. 본 기능은 Turian 모델뿐만 아니라 프로젝트 내 모든 Lego 모델에 적용 가능합니다.

현재로서는 이 기술이 어떻게 작동하는지에 대해 더 깊이 파고들지 않겠습니다. Ali의 발표에서 이미 자세히 다루었으며, 이 발표에서도 다시 언급할 예정입니다. 따라서 recoloring이 훌륭한 기술이라는 점을 기억해 주시기 바랍니다.

그러므로 현재로서는 저희 아티스트들이 여러 툴을 갖추고 있다고 말씀드리면 충분합니다.

시각적 다양성을 위해 바이옴별 타일셋을 활용하여 각 바이옴에 시각적 정체성을 부여합니다.

전용 color palette 및 brick shapes를 사용하여.

예를 들어, forest와 Nora 지역은 보다 부드럽고 둥근 brick tile을 사용하여 친근한 느낌을 주는 반면, desert와 jungle은 더 각진 형태를 사용합니다. 이 외에도 각 레벨마다,

타일 모델 및 타일셋 라이브러리 관리 부담을 늘리지 않고 색상을 덧칠할 수 있습니다.

네, 이것으로 간략한 요약이 되겠습니다.

본 프로젝트의 테레인(terrain) 제작 과정을 설명드립니다. 테레인 툴은 레벨 디자이너와 환경 아티스트가 널리 활용하며, 타일 모델을 수작업으로 배치하는 것에 비해 작업 속도를 획기적으로 단축시켜 줍니다.

고성능 플랫폼에서는 Nanite를 사용하여 대부분의 작업을 효율적으로 처리합니다.

이를 통해 폴리곤 수(polycount)나 드로우 콜(draw call), 모델 중복(overlapped models)에 대한 걱정 없이 작업할 수 있습니다.

여기 저희 draw call visualization에서 보시는 바와 같이, Switch에서는,

고유한 draw call이 상당히 많다는 것을 알 수 있습니다. Switch에서의 draw call 개수가 때때로

각 지형 패치가 여러 개별 타일로 구성되어 있기 때문에, 보시는 것처럼 지형은 천 개의 draw calls에 달하는 매우 높은 수치를 기록하며 이러한 성능 저하에 상당한 영향을 미칩니다.

자동화된 performance dashboard의 스크린샷입니다. 상단은 render thread time, 하단은 draw call을 나타냅니다. 이 값들 간의 상관관계를 보여줍니다.
이 그래프들은 render thread에 부담을 줄이기 위해 draw call 수를 2.5천 이하로 유지하고, render thread가 다른 작업을 수행할 수 있도록 여유 공간을 두어야 함을 시사합니다.
따라서 이 문제는 해결해야 할 과제입니다. 동시에, 아티스트들이 플랫폼별로 다른 레벨을 다시 구축하는 상황을 피하기 위해 기존 workflow는 변경하지 않아야 합니다. 시간과 자원적으로도 이를 감당할 여력이 없기 때문입니다.

현재까지 많은 레벨에서 Vista 및 PlaySpace 등 모든 요소를 고려하여 terrain이 거의 finalized된 상태입니다.

이제 관련 배경 정보에 대해 어느 정도 익숙해지셨습니다.

이제 발표의 핵심 부분인 terrain optimization에 대해 말씀드리겠습니다.

언리얼 엔진 최적화를 위해 아티스트들이 draw call 감소에 많은 노력을 기울였습니다. Switch를 중심으로 레벨 디자인을 시작하고, 이후 더 높은 사양의 플랫폼으로 확장하는 방식을 채택했습니다. 이를 위해 고정 카메라 앵글을 최대한 활용했으며, 가능한 경우 더 큰 타일을 사용했습니다. 예를 들어, 8x8 타일 대신 32x32 또는 16x16 타일을 적용했습니다. 또한, 수동 최적화 작업도 다수 진행하였습니다.

겹치는 Turin 패치나 아주 작은 부분만 보이는 대형 패치의 개수를 최소화하였습니다. 따라서 이 시점까지 아티스트들은 많은 제약 속에서 작업했음을 알 수 있으며, drawcode count를 약 5.6K 수준으로 최적화할 수 있었습니다.

8K와 같은 초기 수치를 고려하면 매우 인상적이지만, 여전히 목표치와는 거리가 있습니다.

시각적 품질 저하를 방지하기 위해 엔지니어의 도움이 필요했습니다. 첫 번째 아이디어는 local instant tiles를 줄이는 것이었습니다. 이는 기존 terrain tool이 patch data를 활용하기 때문에 통합이 간단합니다. 여러 static mesh component를 spawning하는 대신, 단일 hierarchical instant static mesh component를 spawning하여 tile의 모든 instance를 포함할 수 있습니다.

그러나 안타깝게도, 빠른 테스트 결과 이는 사실이 아닌 것으로 나타났습니다.

많은 경우, 인스턴스화되지 않은(non-instance) 버전보다 성능이 더 저하됩니다. 렌더 스레드(render thread) 비용은 특정 레벨에서 매우 높은데, 예를 들어 상단 허브(hub) 레벨에서는 19밀리초(milliseconds)나 증가하는 모습을 보입니다.

렌더 스레드의 새로운 병목 현상은 주로 HSM(Hierarchical Z-Buffer Occlusion)에서 발생하며, 이는 뷰 가시성 작업부터 동적 그림자 작업까지 영향을 미칩니다. 이것이 CPU 측 문제의 일부입니다. GPU 측의 문제는 어떻습니까?

GPU 측면 역시 좋지 않은 상황이며, 이는 높은 primitive count와 관련이 있을 가능성이 있습니다.

때로는 몇 픽셀만 보이고 실제로는 거의 보이지 않음에도 불구하고 전체 instance cluster가 렌더링될 수 있습니다.

HSM은 보시는 바와 같이 몇 가지 추가적인 Vertex Attribute를 가지고 있습니다. 따라서 이러한 모든 Vertex Attribute를 GPU로 가져오는 데 더 많은 Store Cost가 발생합니다.

본 프로젝트에서 Vertex Cost는 매우 중요한 문제입니다. 이를 해결하기 위해 여러 팀이 협력하고 있습니다. 예를 들어, TechArt 팀은 Mesh Complexity를 낮추는 작업을 수행했으며, 렌더링 팀은 16-bit floating point vertex position과 같은 최적화를 적용했습니다. 따라서 Memory Bandwidth에 더 큰 부담을 주는 요인은 신중히 검토해야 합니다.

이것은 다른 모든 최적화를 되돌리는 것과 마찬가지이므로 작동하지 않을 것입니다.

안타깝게도 저희는 이 방향으로 진행하는 것을 중단해야 했습니다. 하지만 엔진의 instancing 처리 방식에 5.3 이후 많은 개선이 있었다고 들었기에, 여러분의 경우는 다를 수 있습니다. 따라서 반드시 본인의 환경에서 profile을 수행해 보시기 바랍니다.

당신의 vertex processing cost는 저희만큼 높지 않아, extra attributes에 대한 비용은 문제가 되지 않을 수 있습니다. 따라서 저희가 구상한 두 번째 아이디어는 terrain patch마다 고유한 Lego model을 생성하는 것입니다. 그러나,

보시는 바와 같이, 이는 다소 복잡한 데이터 변환 과정을 수반하며, 이는 효율적이지 않습니다. 더 중요하게는, 추가되는 에셋의 절대적인 수가 우리를 망설이게 할 것입니다. 시연을 위해, 저희는 약 150개의 레벨에 지형이 포함되어 있으며, 각 레벨당 수십 개의 지형 패치가 있습니다. 이는 수천 개의 LEGO 모델을 가지게 됨을 의미하며, 각 모델은 더 간단한 모델들이 합쳐져 매우 복잡합니다.

전체 프로젝트에 걸쳐 보유하고 있는 모든 타일셋(tilesets)을 종합하여 약 500개의 타일 모델(tile models)을 보유하고 있습니다.

이 모든 것만으로도 에디터 내에서 에셋 사이즈가 약 800MB에 달합니다. 따라서 수천 개의 추가 테레인 모델을 사용하는 것은 매우 부담스럽게 느껴집니다.

이 지점에서, 저희는 한 걸음 물러서서 scene을 살펴보았습니다.

다음은 제가 가지고 있는 예시 장면입니다. 가까이 있는 지형 패치가 렌더링되는 모습입니다.

저희에게 high detail은 문제가 되지 않으나, 멀리 있는 patches의 경우 더 큰 이슈가 됩니다. 또한 depth of field의 도움으로 어차피 blurry하게 보일 것입니다.

세 번째 아이디어인 proxy mesh를 탐구하기 시작했습니다. 이는 언리얼 엔진의 World Partition에서 H-Law 시스템과 유사하며, 멀리 떨어진 패치들을 합치고 단순화된 버전으로 교체하는 방식입니다.

이를 수행하기 위한 첫 번째 퍼즐 조각은 당연히 이러한 meshes를 어떻게 병합하는가입니다. 이를 위한 몇 가지 옵션이 있습니다. Houdini는 아마도 소개가 필요 없을 것입니다. 나머지 두 가지에 대해, 혹시 모르시는 분들을 위해 Mesh Merge Utilities는 엔진의 내장 active merging tool의 로직 툴킷입니다.

Dynamic Mesh는 모델링 툴에서부터 Blueprint나 Python 같은 스크립트를 이용한 지오메트리 조작 기능에 이르기까지, 인게임 메시 편집 작업의 상당 부분을 지원합니다. 따라서 긴 설명을 줄이면, 저희는 dynamic mesh 포맷으로 작업하는 것을 선택하게 되었습니다.

본 기능은 메쉬 병합(mesh-merge) 유틸리티보다 유연성을 제공하면서도 외부 프로세스를 호출하거나 Houdini와 같이 데이터를 오가는(data round trip) 과정이 필요 없어 해당 사용 사례에 두드러집니다. 이에 몇 가지 스니펫을 준비하여 실제 작동하는 모습을 보여드리겠습니다.

이것이 어떻게 진행되는지 살펴보겠습니다. 좋습니다. 여기 첫 번째 스니펫입니다.

개별 타일들을 병합하기 위해 추가했습니다. 이는 매우 간단한데, 앞서 말씀드린 것처럼 dynamic mesh format을 지원하는 많은 빌트인 엔진 연산 기능들이 있기 때문입니다. 여러분이 보시는 몇 가지 geometry script helper functions도 포함됩니다. 이 snippet은 개별 static mesh를 dynamic mesh로 복사하는 작업을 수행합니다. 원본 mesh의 lod를 dynamic mesh로 복사하도록 선택할 수도 있습니다. 이후, 현재의 tile mesh를 merge mesh에 append하며, 이는 작업이 완료되면 static mesh asset으로 복사됩니다. 따라서 이 시점에서 엔진에서 바로 사용할 수 있습니다.

이로써 첫 번째 퍼즐 조각을 맞추었습니다. 개념 설명을 위해 `lot zero`를 사용하였으나, 실제로는 `polycount`를 줄이기 위해 가장 높은 `lot`을 사용합니다.

그래서 퍼즐의 두 번째 부분은 런타임에 병합된 메쉬를 실제로 표시하는 것입니다. 지형 패치의 bounding box를 기반으로 하는 매우 간단한 알고리즘을 사용하여 크기가 특정 임계값 이하이면 작다고 결정합니다. bounding box의 모든 버텍스가 far depth of field plane 뒤에 있다면,

왼쪽 하단의 이미지와 같이 가상의 평면이 있다면, 이는 멀리 떨어진 것으로 간주됩니다. 따라서 패치가 뷰포트에서 완전히 벗어나 있거나, 멀리 있고 작을 경우 프록시 모드로 표시하게 됩니다. 프록시 모드 표시는 개별 타일을 숨기고 메시(mesh)를 표시하는 것을 의미합니다. 이는 일정한 심도(depth of field) 거리로 기기에서 수치를 테스트하기 위한 매우 빠른 프로토타입을 만드는 데 도움이 됩니다. 이곳은 프로젝트에서 지형적으로 가장 복잡한 허브 레벨에 있는 모든 튜린(Turin) 패치를 테스트하는 레벨입니다.

캐릭터의 위치에 따라 draw thread cost는 0.5ms에서 2.5ms 사이로 감소하고 있음을 확인할 수 있습니다. GPU 시간과 triangle count는 예상대로 증가하나, 개선 방안을 모색 중입니다. 첫 번째 아이디어는 패치가 상당한 크기일 수 있지만, viewport에 보이는 부분은 극히 일부에 불과하다는 점을 활용하는 것입니다.

큰 메쉬를 전송하는 것은 다소 낭비입니다. 따라서 여기서의 문제는 이전에 겪었던 instancing cluster problem과 유사합니다.

CPU 측 쿨링으로는 해당 메쉬가 GPU로 렌더링하기에 가치가 있는지 안정적으로 판단하기 어렵습니다. 따라서 패치를 더 작은 청크로 분할하여 이 과정을 도울 수 있습니다.

이것만으로도 polycount와 GPU time을 base level에 가깝게 낮추는 데 도움이 됩니다. primitive count를 살펴보시면,

TechArt 팀이 Heiger Lords의 더욱 최적화된 버전을 출시함에 따라, poly count는 더욱 감소될 것으로 예상됩니다. 따라서 이 부분에 대해서는 크게 우려하고 있지 않습니다.

네, 이제 이 방향의 성능 특성에 만족합니다.

아티스트 워크플로우에 영향을 주지 않으면서 이 문제를 해결하는 방안을 고려할 수 있습니다. 현재 저희가 가진 기능으로는 언리얼 엔진의 HLOD가 프록시 메쉬를 처리하는 방식과 유사하게 auto-regenerate unsafe 메커니즘을 쉽게 적용할 수 있습니다.
레벨의 모든 것을 unsafe하게 생성하는 것은 시간이 오래 걸리고 아티스트들의 불만을 야기할 수 있습니다. 이를 방지하기 위해 테레인 패치의 checksum을 저장하여 편집 중 변경된 부분을 신속하게 파악할 수 있습니다.

매일 밤 Team City job을 통해 게임의 모든 레벨을 스캔하며 필요한 경우 terrain patches를 업데이트하고 있습니다. 때때로 이 job을 수동으로 트리거해야 할 때도 있습니다.

이러한 proxy mesh는 강제로 재생성될 수 있습니다. 예를 들어, 코드나 데이터에 일부 breaking changes를 적용하고자 할 때 사용할 수 있습니다. 다만, 이는 주로 야간에 수행하는 작업입니다.

이러한 불편함은 가끔 감수할 수 있습니다.

애셋 크기에 좀 더 신경 쓰고자 여러 가지 방법을 적용하였습니다. 앞서 언급했듯이, 가장 중요한 점은...

이 단계에서는 Techout 팀의 작업 덕분에 매우 최적화되었습니다. 하지만 메시가 병합되면 추가적인 축소 설정을 위한 여지가 항상 존재합니다.

사용되지 않는 UV channel도 제거됩니다. 이에 대해서는 잠시 후에 다시 설명하겠으나, 우선 다른 옵션들을 살펴보겠습니다. 생성된 mesh에서 ray tracing acceleration structure도 제거할 수 있습니다. 닌텐도 스위치에서 실시간 ray tracing을 구현할 계획이 없기 때문입니다. 다음으로, navigation과 collision 역시 필요 없습니다. 이 mesh들은 단순히 화면에 표시되는 용도이기 때문입니다.

아주 먼 거리에서는 캐릭터가 타일에 서 있지 않으며, furthermore, navigation 및 collision이 활성화된 원래 타일들은 여전히 존재하지만 숨겨져 있습니다. 또한, 프로젝트 초기에 switch에서도 mesh distance field를 사용하지 않기로 결정하였습니다. 기억하기로는, base pass에 약 2.5밀리초 정도의 추가적인 성능 부하가 있었습니다.

이것과 유사한 맥락으로, 해당 부분도 함께 적용됩니다. 여기서 핵심은 마지막 네 가지 항목이 기본적으로 활성화되어 있다는 점입니다. 이는 쉽게 놓칠 수 있으며, 불필요하게 많은 카트리지 공간을 차지할 수 있습니다.

이 부분은 주목할 만한 내용입니다. 모든 asset의 크기를 예를 들어 90% 절감할 수 있습니다. 예를 들어, Hub의 모든 proxy meshes는 100MB에서 8MB로 줄었습니다.

이 모든 것을 통해 아이디어에 대한 확신을 더하고, 품질과 정확성 측면에서 남아 있는 문제들을 마무리할 수 있습니다.

따라서, 저희가 처음에 발견한 문제는 이러한 텍스트의 재채색된 Turin 패치의 정확성입니다. 이 기능은 여러분께서 아직 기억하고 계시리라 희망합니다.

이번에는 Recolor 기능이 어떻게 작동하는지 간단하게 설명드리겠습니다. Houdini 메시 파이프라인에서는 Lego color ID를 UV channel 1에, brick ID는 vertex의 UV channel 2에 저장합니다.

모델의 색상이 변경될 경우, 해당 모델의 Scene Proxy는 어떤 색상이 최종적으로 적용될지를 지정하는 Lookup Buffer를 갖게 됩니다. 이후, UV1에서 가져온 Color ID는 기본 색상으로 사용됩니다.

recoloring buffer에서 값을 가져오거나, 이를 사용하여 material parameter collection의 실제 RGB 값을 조회합니다. 따라서 proxy mesh의 경우, 이러한 동적 조회를 원하지 않습니다. 모든 것을 UV1에 기록하고 기존 material을 재사용할 것입니다.

런타임 렌더링 비용 절감 외에도, UV2를 제거함으로써 메모리 사용량 및 패키지 사이즈 또한 줄일 수 있습니다. 이는 앞서 언급했던 불필요한 UV 제거 작업의 일환입니다.

기타 사항들도 있습니다. 그래서 제가 사용할 테스트가 있습니다. 이것은 최악의 시나리오 테스트와 같습니다.

저희 아티스트들은 임의의 색상 스트립으로 칠하는 대신 주로 Bucket Coloring을 사용하여 테렌(terrain)을 작업하기 때문입니다. 따라서 이 테스트 케이스가 통과된다면 프로덕션에서도 문제가 없을 것입니다.

메시 머징(mesh merging) 코드에 대해 다시 말씀드리겠습니다. 현재까지는 다이내믹 메시(dynamic mesh)가 단순한 유틸리티인 메시 머지와 비교했을 때 특별히 두드러지지 않았습니다. 하지만 이제부터는 다음과 같은 기능을 제공하며 차별점을 두기 시작합니다.

이것은 mesh editing mechanism에 관한 것입니다. edit mesh 함수는 매우 유연하며, mesh에 어떤 작업을 수행할지 정의하기 위한 lambda 함수를 요구합니다. 여기 edit mesh 함수에 전달되는 lambda 함수의 예시가 있습니다.

이 로직은 이전에 논의했던 내용과 같습니다. UV2에서 BrickID를 사용하여 예상 색상 값을 가져온 다음, recoloring buffer를 통해 이 값을 UV1에 기록합니다. 매우 간단한 함수 호출만으로 엔진 내에서 mesh를 조작할 수 있습니다.

현재는 이 부분에 대해 표면적인 이해만을 하고 있습니다.

Dynamic Mesh는 매우 강력한 포맷이며, 잠재력이 매우 큽니다. 따라서 persistent vertex 및 triangle ID를 통해 element에 접근하도록 하는 것이 좋은 팁입니다. 그 이유는 동일한 vertex가 다른 UV 레이어에서 다른 element ID에 존재할 수 있기 때문입니다. 만약 동일한 element ID를 사용한다고 가정하면

UV 레이어가 달라지면 동일한 버텍스에 접근할 때 예상치 못한 결과가 발생할 수 있습니다. 제가 직접 실수를 통해 이를 깨닫게 되었습니다.

이것이 오류가 발생하는 모습입니다. 최종 vertex color가 잘못된 element에 기록되어 결과 메시(mesh)에 V 모양의 삼각형이 나타나게 됩니다.

이전에는 DOF distance가 항상 일정했으나, 이번에 동적인 draw distance를 개선했습니다.

본 기능은 저희가 개발한 우수한 카메라 프레임워크를 활용하고자 합니다. 이 프레임워크는 2인 협동 모드의 플레이어 1과 2, 맵 상의 흥미로운 지점, 또는 전투 중인 오브젝트 등 다양한 요소의 영향을 동적으로 받아들이는 것이 특징입니다.

이것은 디자이너들이 월드를 선보일 수 있는 다양한 튜닝 옵션을 가지고 있기 때문에 매우 커스터마이징이 가능합니다.

더불어, 일반적인 맵 탐험부터 전투, 보스전, 시네마틱 등 매우 광범위한 문맥을 포괄합니다.

매우 꼼꼼합니다.

동적 거리가 필요합니다. 생각보다 쉬운 것으로 드러났습니다.

모든 것이 결국 내장된 scenic camera component로 귀결되기에, 해당 component는 이미 모든 종류의 정보를 가지고 있습니다. 따라서 DOF post-process shader에서 이 formula를 활용하고, DOF distance가 noticeable popping을 피할 만큼 충분히 멀리 있도록 buffer distance를 삽입하였습니다. 이 buffer distance는 전적으로 optional입니다.

그것을 사용하시거나 사용하지 않으실 수 있습니다.

동적 그리기(dynamic draw)가 작동하는 모습입니다.

보시다시피, 보라색 plane은 움직입니다.

다양한 카메라 상황에서

각 프레임의 카메라 설정에 따라.

거의 완료되었습니다. 최종 단계 테스트를 위해 여러 레벨에 이 takeout을 롤아웃하기 시작했습니다. 프로젝트의 hub 레벨이나 몇몇 핵심적인 core bound 레벨부터 시작했으며, performance dashboard를 통해 이를 확인했습니다. 관련 performance metric이 밤사이 향상되는 것을 확인하여 매우 만족스러웠습니다.

프로젝트에 생성된 모든 terrain proxy meshes에 대해 약 90MB가 추가되었습니다. 이는 런타임 성능 향상을 고려할 때 여전히 수용 가능한 수준입니다. 이제 다른 레벨에도 이러한 기능을 적용하여 전반적인 승리를 늘리는 것이 과제입니다.

마무리하기 전에 흥미로운 문제를 하나 소개하고자 합니다. 일부 레벨에서 proxy meshes를 켜고 끄는 Turing proxy runtime code가 end of frame hitches를 유발한다는 것을 관찰하였습니다.

이것은 많은 Static Mesh Component가 항상 Visible로 설정되고 보이는 밀집된 영역에서도 재현 가능합니다. 따라서 Set Visibility는 정확히 공짜가 아니라는 것이 밝혀졌습니다.

다음은 각 단계별로 발생하는 과정을 설명해 드리겠습니다.
첫째, terrain system에서 visibility 호출이 발생합니다. 이러한 호출은 개별적으로 처리되지 않으며, game thread는 프레임 끝에서 두 번째 단계와 같이 이를 일괄 처리하여 render thread로 전송합니다.
셋째, render thread는 이러한 요청을 실제로 처리합니다.

이 과정은 먼저 invisible로 설정되는 컴포넌트들의 proxy를 deallocate하고, visible로 설정되는 컴포넌트들의 새로운 proxy를 allocate하고 설정함으로써 이루어집니다. 그 결과, 게임 스레드는 렌더 스레드가 작업을 완료할 때까지 상당한 시간을 기다려야 하는 네 번째 상황을 맞이하게 됩니다.

모든 프레임에서 컴포넌트의 visible/invisible 설정을 제한하는 것이 명백한 해결책입니다. 혹은, 더 전문적인 용어로는 staggering update라고도 불립니다. 그러나 여기서 대부분의 프로그래머들이 던질 질문이 있습니다. 이것보다 더 나은 방법은 없을까?

저는 언리얼 코드 기반을 탐색하는 여정을 시작했습니다. 렌더 스레드의 여러 패스에서 사용되는 Scene View의 primitive visibility map이라는 유용한 것을 발견했습니다.

Occlusion culling 또는 fading primitive와 같은 기법과 자연스럽게 결합되는 것으로 보입니다. SceneView extensions를 통해 override할 수 있는 custom visibility pass를 시도해 보았습니다. 이 pass는 렌더 스레드의 적절한 시점에서 실행됩니다.

terrain system에서는 set visibility 호출을 사용하지 않으며, 대신 custom scene extension을 사용하여 components를 보이거나 숨기는 방식으로 작업합니다.

모든 컴포넌트 프록시(component proxies)가 렌더 스레드(render thread)에 계속 살아있도록 하는 것이 핵심 아이디어입니다. 이를 통해 저는 이러한 씬 프록시(scene proxies)를 할당하고 설정하는 데 드는 비용을 더 이상 지불하지 않아도 되기를 기대합니다.

이따금씩, 렉이 덜 걸릴 것입니다. 그래서 답은 '예, 그리고 아니오'입니다.

덜 버벅거리게 됩니다. 문제는 해결된 셈이지요. 하지만 자세히 보면, 각 프레임마다 더 많은 primitive를 처리하기 때문에 기본 비용이 훨씬 높아집니다. 따라서 custom visibility 부분을 cooling passes 이전이나 이후로 옮기는 등의 다른 해결책을 시도해 볼 수 있습니다.

이 문단은 렌더링 성능 최적화와 관련된 내용을 다루고 있습니다. 'primitives' 처리 수를 줄이는 것이 근본적인 해결책이 되지 못했으며, 'RealDefi' 테스트 결과에 따르면 처리해야 할 'primitives'의 총량이 너무 많기 때문임을 설명합니다.

또한 런타임 메모리(runtime memory)에 약 40MB가 소모됩니다. 따라서 앞선 질문에 대한 답변은 안타깝게도 불가능합니다.

알겠습니다. 이제 설명이 모두 끝났습니다. 이 부분은 상당히 오래전에 다루어졌던 내용입니다.

본사에서는 아주 적은 품질 저하만으로 performance target를 달성하였습니다. 우리 아티스트들 역시 육안으로 popping을 인지하지 못할 정도로 우수합니다. 최종 package 용량이 약간 증가하는 것이 유일한 대가이나, 이는 크게 문제 될 수준은 아닙니다. 또한, 대부분의 과정이 자동화되어 있습니다.

이로써 아티스트의 오버헤드가 전혀 발생하지 않아, 아티스트들은 자신들이 잘하는 것에 집중할 수 있습니다.

결론적으로, 저희는 모든 목표와 제약 조건을 고려하여 특정 문제를 해결하는 과정을 단계별로 설명해 드렸습니다. 때로는 실패한 실험에 부딪히기도 하였으나, 이는 지극히 당연한 과정이라 사료됩니다.

때로는 예상대로 작동하지 않는 방법을 아는 것이 도움이 되기도 합니다. 최적화 실험에 있어서 반복적인 시도만큼 좋은 것은 없다고 생각합니다. 따라서 이 과정에서 보셨듯이 저희는 이를 자주 반복했으며, 결과를 분석적으로 검토하여 다음 단계를 안내받았습니다.

또한 보시는 바와 같이, 저희의 솔루션들은 결코 복잡한 기술이 아닙니다. 단순히 작고 간단한 해결책들을 엮어 하나의 큰 작동하는 솔루션으로 만들어내는 것입니다. 하지만 이러한 방법들이 우리가 찾고 있는 해답이 될 것입니다. 마지막으로, 감성적인 한마디를 덧붙이자면, 최적화는 팀의 노력입니다.

이 문제는 Switch 버전 게임 개발 과정에서 해결해야 했던 수많은 과제 중 하나입니다. 이 과정에는 Tech Art, Environment Art, Engineering 등 다양한 분야의 인력들이 참여했습니다. 이 자리를 빌려 그들의 노고에 깊이 감사드립니다.

훌륭한 팀원이 되고 이 모든 과정을 즐겁게 만들겠습니다. 아, 거짓말이었습니다.

마지막 팁은 name collisions를 피하기 위해 primitive나 proxies와 같은 일반적인 이름으로 무엇이든 명명하지 않는 것입니다. 이를 통해 동료들이 다양한 컨퍼런스에서 진행했던 발표에 대한 정보를 찾아보실 수 있습니다.

오늘 참고 자료로 사용한 다이어트 정보는 온라인에서도 찾아보실 수 있습니다. QR 코드를 통해 접속하실 수 있습니다.

다운로드하실 수 있도록 QR 코드를 준비하였습니다. 질문 시간을 충분히 갖지 못하더라도, 언제든지 LinkedIn을 통해 저에게 연락 주시면 됩니다. 감사합니다.

경청해주셔서 감사합니다.