<aside> <img src="/icons/reorder_blue.svg" alt="/icons/reorder_blue.svg" width="40px" /> 목차
</aside>
2022년 4월, 드디어 학수고대하던 Unreal 5가 출시되었다. 필자는 Unreal 5에서 가장 흥미롭게 바라본 기능은 바로 Nanite이다. 기회가 된다면, 정말 공부해보고 싶은 새로운 렌더링 파이프라인 Nvidia의 “Mesh Shader”를 세상에 상용화가 된 것이다.
필자는 새로운 파이프라인이 점진적으로 업계의 워크플로우를 바꿀 것이라고 생각한다. 파격적인 최적화 방식이 모든 워크플로우를 바꾸긴 어렵겠지만 추후 업데이트될 기능들을 생각한다면, 이러한 파이프라인에 맞게 조사와 연구가 필요하다고 생각했다.
때문에, 직접 Unreal Engine에서 Nanite를 가지고 실험하며 이야기해보도록 하자.
<aside> 📌 필자는 Nanite와 Mesh shader를 이해하는 데에 아래 링크의 글이 많은 도움이 되었다.
</aside>
Nanite를 알아보기 전에 Nvidia의 “Mesh Shader”에 대해서 이해를 짚고 넘어가야 한다. Nanite가 꽤나 혁신적인 기술처럼 보이지만 이 또한 Msh Shader를 응용하는 형태인 것이며 Unreal 5가 출시되기 전에 이미 유비소프트 등에서 사용된 방법이다.
일단 결론적으로 말하자면, Mesh Shader가 효용성이 더 좋은 이유는 카메라 밖으로 벗어난 오브젝트를 컬링하고, 거리에 따른 LOD를 동적으로 처리하는 데에서 기존 파이프라인보다 빠르기 때문이다.
Nvidia의 Mesh Shader

각 파이프라인의 처리와 취소
기존 파이프라인의 단점은 데이터를 읽고 Vertex Shader에서 이미 처리된 상태에서 Geometry 파트 혹은 Fragment Shader 파트에서 취소를 할 수 있다. 그러나 이미 Vertex Shader에서 연산을 처리하는 과정에서 비용이 소모되었기 때문에, 최적화 부분에서는 걸림돌이라고 볼 수 있다.
때문에, 기존 파이프라인에서는 Mesh Component 마다 LOD를 적용하여, 거리에 따라 Mesh를 바꾸는 형태로 최적화를 진행한다. 이 방법의 가장 큰 문제는 애셋 제작 비용이 많이 드는 것이다.
반면, Mesh Shader는 Task Shader, Mesh Shader 두 단계에서 모두 취소가 가능하다. Vertex Shader 파트와 달리 첫 단계인 Task Shader는 Vertex들을 묶어서 관리되는 Cluster(Meshlet)를 카메라 안에서 식별하고 Mesh Shader에 Buffer를 전송한다. Cluster 단위를 검사하기 때문에, 검사의 연산 횟수는 줄어들고 하나의 검사로 몇 백의 Vertex를 Culling할 수 있다.
Unreal의 Nanite

자 그러면 Nanite는 Mesh Shader를 어떻게 활용할까? 이 부분을 알기 위해서는 3가지 주요 기능을 살펴봐야한다.
Nanite Build에서 기존 Mesh Data를 Decimate 처리하여 각 Cluster 그룹마다 Vertex LOD를 관리하며, 계층구조를 이룬 Cluster는 카메라 거리에 따라, 동적으로 Mesh의 LOD가 전환되며 렌더링에서 최적화를 할 수 있는 것이다.
.gif)
실험 애셋 바위 모델링 | tris 426k
실험을 위해, 간단하게 Blender에서 바위 오브젝트를 제작해보았다. 약 43만 트라이앵글로 구성된 오브젝트로 곧바로 Unreal으로 Import한 후, Nanite Build를 적용해보았다.


Nanite Build 옵션에서 트라이앵글 퍼센트 유지를 100% 적용하면, Blender에서 제작했을 때의 Tris 카운트가 똑같다.

트라이앵글 퍼센트 - 5%

트라이앵글 퍼센트 - 100%
트라이앵글 퍼센트 옵션을 통해, Nanite Mesh가 퍼센트에 맞게 Decimate를 진행한다.
실험에서 쓰인 애셋은 단순한 암석 애셋이기도 하고, 기본적으로 43만 트라이앵글인 Mesh이기 때문에 Decimate 과정에서도 큰 손실이 없는 것으로 보인다.

혹시 그렇다면, Decimate 과정에서 UV맵의 오차 손실은 어떨까? Blender에서 텍스쳐맵이 보기 쉽게 UV 언랩을 진행해보았다.

트라이앵글 유지 - 5%

트라이앵글 유지 - 100%
결과는 매우 만족스러웠다. Decimate 처리 과정에서도 UV가 잘 보존되어 있었다.
이렇게 본다면, ZBrush에서 하이폴리곤 작업을 진행한 후, 곧바로 Unreal에서 Nanite Build에서 Decimate 처리로도 충분히 시간을 아낄 수 있다고 본다.
디테일한 묘사가 들어간 하이폴리곤에서는 어떻게 Decimate가 될 지는 추후에 따로 실험해보도록 하겠다. 크게 변형되지 않는다면, 워크플로우의 시간 절약이 효용적일 것이다.
.gif)
Nanite 예비 메시
Nanite Mesh는 기존 Mesh와는 다르다. Nanite 렌더링 파이프라인이 지원되지 않는 경우에는 별도로 기존 Mesh를 활용해야 하는 데, 바로 이 때 쓰이는 Mesh가 예비 메시이다.
예비 메시를 사용할 때에는 기존 렌더링 파이프라인을 거치기 때문에, 기존 폴리곤 최적화 문제가 나올 수 밖에 없다. 때문에, Nanite에서는 기본적으로 오차 거리를 0.5로 잡는다. 이로 인해 디테일한 Topology까지 뭉개지는 형태가 나온다.
앞서 설명한 트라이앵글 LOD와 Cluster를 Nanite 시각화 뷰를 통해서 디버깅할 수 있다.
트라이앵글도 디버깅에서 유용하겠지만, 시각적으로 가장 명확하게 구분이 가는 것은 아무래도 Cluster이지 않을까 싶다.

같은 드로우콜 조건에서의 프레임과 LOD에 따른 폴리곤 성능을 확인해보도록 하자.
위 이미지대로 같은 암석 애셋을 2,000개 놓은 환경에서 실험하였다.
제목 없는 동영상 - Made with Clipchamp.mp4
첫 번째로, Nanite Build가 안된 일반 Mesh로 진행해본다.
결과는 처참했다. Triangle Drawn 수치가 기하급수적으로 오르다가 마이너스(-)까지 표기되는 버그 지경에 이른다. 아무래도 화면에 나타난 갯수 만큼 43만 폴리곤을 곱한 값이었을 것이다.
마우스를 아무리 움직여봐도, 프레임 드랍으로 반응 조차 하지 않았다.
제목 없는 동영상 - Made with Clipchamp (1).mp4
두 번째로, Nanite Mesh로 진행해보도록 하자.
화면을 이동하거나, 화면을 줌 인&아웃을 하더라도 Triangle Drawn이 6000~8000을 유지하는 것을 볼 수 있다. 더군다나 기존 Mesh는 마우스가 움직여도 반응하지 않는 반면, Nanite Mesh는 부드럽게 작동된다.