3D 게임에서는 화면에 보이는 모든 물체를 항상 최고 품질의 Mesh로 렌더링할 필요가 없다.
카메라 바로 앞에 있는 캐릭터는 세밀한 Geometry가 필요하지만, 수백 미터 떨어진 건물이나 나무는 높은 Polygon 수를 사용해도 화면에서 거의 차이가 보이지 않는다.
이때 사용하는 대표적인 최적화 기법이 LOD(Level of Detail)다.
카메라 가까움 -> 고해상도 Mesh -> 많은 Triangle
카메라 멂 -> 저해상도 Mesh -> 적은 Triangle
Unity 6.2에서는 기존 LOD 시스템과 별도로 Mesh LOD라는 새로운 방식이 추가됐다. Unity는 이를 Mesh 내부에 LOD를 포함시키는 방식으로 제공하며, GPU의 Vertex 처리 부담을 줄이면서 추가 메모리 사용량을 최소화하는 것을 목표로 한다.
LOD가 필요한 이유
GPU는 Mesh를 렌더링하기 위해 Vertex와 Triangle 등의 Geometry 데이터를 처리한다.
문제는 물체가 화면에서 작게 보이더라도 원본 Mesh의 모든 Triangle을 그대로 처리한다는 것이다.
예를 들어 100,000개의 Triangle을 가진 나무가 있다고 하자.
카메라가 가까울 때는 높은 Polygon 수가 의미가 있다.
하지만 나무가 화면에서 몇 픽셀 정도로 보이는 거리라면 100,000개의 Triangle을 유지할 이유가 없다.
고해상도 Mesh -> 화면에서 보이는 정보 감소 -> 불필요한 Geometry 처리 증가
LOD는 이 문제를 해결한다.
카메라 거리 증가 -> LOD 단계 감소 -> Triangle 감소 -> GPU Geometry 처리량 감소
Unity 역시 LOD를 카메라와의 거리가 증가할수록 렌더링해야 하는 Triangle 수를 줄이는 최적화 기법으로 설명한다.
기존 Unity LOD
기존 Unity에서는 주로 LOD Group을 사용한다.
하나의 모델에 여러 단계의 Mesh를 준비한다.
LOD 0 -> High Poly
LOD 1 -> Medium Poly
LOD 2 -> Low Poly
그리고 LOD Group이 카메라와의 거리에 따라 어떤 LOD를 사용할지 결정한다.
구조적으로는 각각의 LOD가 별도의 GameObject와 Mesh Renderer를 가지고 있는 형태다. Unity는 필요한 LOD GameObject를 표시하고 나머지를 숨긴다.
개념적으로 보면 다음과 같다.
Model -> LOD 0 Mesh / LOD 1 Mesh / LOD 2 Mesh
따라서 LOD를 여러 단계로 만들수록 각각의 Mesh 데이터가 필요하다.
Mesh에는 Vertex Buffer와 Index Buffer가 존재하기 때문에 LOD Mesh를 별도로 만들면 각 Mesh가 자신의 Geometry 데이터를 가지고 있어야 한다.
결국
LOD 0 Mesh + LOD 1 Mesh + LOD 2 Mesh
형태로 데이터가 증가한다.
Unity 6.2 Mesh LOD
Mesh LOD는 접근 방식이 다르다.
핵심은 LOD를 별도의 완전한 Mesh로 저장하는 대신 기존 Mesh 내부의 Index Buffer를 활용하는 것이다.
개념적으로 보면
Original Mesh -> Vertex Buffer + Index Buffer
였던 구조가
Mesh -> Vertex Buffer + LOD별 Index Range
형태로 확장된다.
Mesh LOD 생성기는 원본 Mesh의 Geometry를 분석하고 Triangle을 단순화한다.
그리고 각각의 LOD에서 사용할 Triangle의 Index를 기존 Mesh의 Index Buffer에 추가한다.
중요한 점은 LOD마다 새로운 Vertex Buffer를 생성하지 않는다는 것이다.
Unity의 공식 문서에 따르면 Mesh LOD의 모든 LOD는 동일한 Vertex Buffer를 재사용하고, 각 LOD는 Index Buffer의 서로 다른 영역을 사용한다.
개념적으로 표현하면
Vertex Buffer -> 모든 LOD가 공유
Index Buffer -> LOD 0 / LOD 1 / LOD 2 ...
가 된다.
왜 Index Buffer를 사용하는가
Mesh를 단순화한다고 해서 반드시 새로운 Vertex를 만들어야 하는 것은 아니다.
Triangle을 구성하는 Index를 줄이는 것만으로도 더 적은 Geometry를 렌더링할 수 있다.
예를 들어 원본 Mesh가
Vertex -> 10,000
Triangle -> 20,000
이라고 하자.
LOD를 낮추면서 일부 Triangle을 제거하면
LOD 0 -> 20,000 Triangle
LOD 1 -> 10,000 Triangle
LOD 2 -> 5,000 Triangle
처럼 사용할 Index의 수를 줄일 수 있다.
이 과정에서 Vertex Buffer 자체는 공유할 수 있다.
따라서
LOD 생성 -> Vertex Buffer 공유 -> LOD별 Index 범위 사용
이라는 구조가 가능해진다.
Unity의 Mesh LOD 생성기는 각 LOD를 이전 단계의 LOD를 기반으로 반복 생성하며, 일반적으로 다음 LOD로 갈수록 사용하는 Index 수를 줄인다.
기존 LOD와 Mesh LOD의 차이
핵심 차이는 LOD 데이터를 저장하는 방식이다.
기존 LOD Group -> LOD마다 완전한 Mesh
Mesh LOD -> 하나의 Mesh가 Vertex Buffer를 공유하고 LOD별 Index를 사용
이를 정리하면 다음과 같다.
| 구분 | 기존 LOD Group | Mesh LOD |
|---|---|---|
| LOD 관리 | LOD Group | Mesh 기반 |
| LOD Mesh | 별도 Mesh | 기존 Mesh 내부 데이터 활용 |
| Vertex Buffer | LOD마다 별도 | 공유 |
| Index Buffer | LOD마다 별도 | LOD별 범위 사용 |
| GameObject | LOD마다 필요 | 구조 단순화 가능 |
| 메모리 | 상대적으로 증가 | 추가 메모리 사용 최소화 |
| 목적 | 일반적인 LOD 관리 | Mesh 기반 효율적인 LOD |
Unity는 Mesh LOD가 기존 LOD Group보다 작은 메모리 사용량을 목표로 하는 이유로 이 구조를 설명한다. LOD Group은 각 LOD마다 Vertex와 Index Buffer를 포함한 완전한 Mesh와 추가 GameObject 및 Component를 보관해야 하지만, Mesh LOD는 Index Buffer에 LOD 데이터를 저장하고 Vertex Buffer를 공유한다.
Mesh LOD의 실제 동작
Mesh LOD의 전체적인 흐름은 다음과 같다.
원본 Mesh -> Mesh LOD 생성 -> Geometry 분석 -> Triangle 단순화 -> LOD별 Index 생성 -> Mesh에 LOD 데이터 저장 -> Runtime에서 적절한 LOD 선택 -> Rendering
런타임에서는 카메라와 물체의 화면상 크기나 거리 등을 기준으로 적절한 LOD를 선택한다.
Object 가까움 -> LOD 0 -> 많은 Triangle
Object 멂 -> LOD 1 -> 적은 Triangle
Object 더 멂 -> LOD 2 -> 더 적은 Triangle
결과적으로 멀리 있는 Object일수록 GPU가 처리해야 하는 Geometry가 감소한다.
Mesh LOD와 Culling은 다르다
LOD를 이해할 때 반드시 구분해야 하는 것이 Culling이다.
둘 다 Rendering 최적화에 사용되지만 해결하는 문제가 다르다.
Culling은 렌더링 자체를 하지 않는 것이다.
카메라 시야 밖 -> Culling -> Rendering하지 않음
LOD는 렌더링하되 Geometry를 단순화하는 것이다.
카메라 시야 안 -> LOD 선택 -> 단순화된 Mesh Rendering
따라서
Culling -> Rendering 대상 제거
LOD -> Rendering 대상의 Geometry 비용 감소
라고 이해하면 된다.
실제 게임에서는 둘을 함께 사용하는 것이 일반적이다.
Scene Object -> Visibility/Culling -> 보이는 Object -> Mesh LOD -> Geometry 감소 -> Rendering
메모리 측면
Mesh LOD의 중요한 특징은 LOD 데이터가 Index Buffer를 중심으로 저장된다는 것이다.
Unity 공식 문서에서는 Mesh LOD의 추가 메모리 사용량이 원본 Index Buffer 크기의 최대 100% 수준이며, LOD 개수 제한이나 홀수 단계 제거 등의 설정을 사용하면 증가량을 더 줄일 수 있다고 설명한다.
기존 방식처럼 LOD마다 Vertex Buffer와 Index Buffer를 포함하는 완전한 Mesh를 추가하는 것과 비교하면 차이가 크다.
LOD Group -> Mesh 전체 복제에 가까운 데이터 증가
Mesh LOD -> Index 데이터 중심으로 증가
Mesh에서 Index Buffer는 일반적으로 전체 Geometry 데이터 중 상대적으로 작은 부분이기 때문에 Mesh LOD가 메모리 측면에서 유리해질 수 있다.
모든 Mesh에 적합한 것은 아니다
Mesh LOD가 모든 모델에 동일하게 잘 적용되는 것은 아니다.
Unity의 Mesh LOD 생성기는 Mesh의 Edge와 Triangle을 분석해 단순화하기 때문에 Mesh 형태에 따라 결과가 달라질 수 있다.
특히 유기적인 형태나 비교적 평평하고 Vertex 밀도가 높은 Mesh에서 잘 동작하며, 곡률이 높거나 서로 분리된 Geometry가 많은 Mesh에서는 원하지 않는 결과가 발생할 수 있다고 Unity가 설명한다.
따라서 자동 생성된 LOD를 무조건 사용하는 것이 아니라 실제 모델에서 결과를 확인해야 한다.
Mesh Import -> Mesh LOD 생성 -> Visual Quality 확인 -> Artifact 확인 -> LOD 설정 조정
이 과정이 필요하다.
Mesh LOD의 의미
Mesh LOD의 핵심은 단순히 “Unity에 LOD 기능이 하나 더 생겼다”가 아니다.
기존 LOD가
여러 Mesh -> 여러 GameObject/Renderer -> LOD Group으로 전환
하는 구조였다면,
Mesh LOD는
하나의 Mesh -> 공유 Vertex Buffer -> LOD별 Index -> Runtime에서 선택
이라는 방향으로 LOD의 데이터 구조를 바꾼 것이다.
결국 목표는 명확하다.
같은 화면 품질 -> 더 적은 Triangle -> 더 적은 GPU Geometry 부담 -> 더 적은 추가 메모리
Unity 6.2에서 Mesh LOD가 추가된 이유도 여기에 있다. Unity는 Mesh LOD를 GPU의 Vertex 처리 부담을 줄이면서 추가 메모리 사용량을 최소화하기 위한 새로운 Runtime LOD 시스템으로 소개했고, 6.2 Alpha 8부터 Mesh LOD 생성 및 렌더링 기능을 제공하기 시작했다.
결국 Mesh LOD는 새로운 LOD의 개념이라기보다는 기존 LOD의 문제를 Mesh 데이터 구조 수준에서 다시 설계한 기능에 가깝다.
기존 LOD -> 여러 완전한 Mesh를 관리
Mesh LOD -> 하나의 Mesh에서 LOD 데이터를 효율적으로 관리
이 차이가 Unity 6.2 Mesh LOD를 이해하는 핵심이다.