에셋과 레벨 최적화 기법
메시 LOD·텍스처 크기·머티리얼 복잡도와 레벨 스트리밍을 조정해 체감 품질을 지키면서 렌더링 비용을 줄입니다.
병목을 찾는 도구를 익혔다면, 이제 실제로 비용이 큰 에셋을 줄여야 성능이 올라갑니다. 같은 장면이라도 메시의 밀도와 텍스처의 크기, 재질의 복잡도, 레벨의 구성 방식에 따라 프레임이 크게 달라집니다. 그런데 화질을 무작정 낮추면 성능은 좋아져도 장면의 매력이 사라집니다.
이 글에서는 메시, 텍스처, 재질, 레벨을 차례로 보며 바로 적용할 수 있는 최적화 기법을 정리합니다. 핵심은 플레이어가 알아차리기 어려운 곳부터 비용을 줄이는 순서를 잡는 것입니다. 텍스처 메모리를 계산해 보며 줄이는 효과를 숫자로 확인하고, 바꾼 뒤에 무엇을 비교해야 하는지도 함께 다룹니다. 이 글을 마치면 어떤 항목을 얼마나 줄일지 측정한 값을 바탕으로 정할 수 있습니다.
에셋과 레벨 최적화의 목표
에셋과 레벨의 최적화는 여러 비용을 함께 줄이는 일입니다. 먼저 렌더링 명령을 제출하는 비용으로, 그리기 호출과 재질 섹션의 수, 렌더링 상태의 변경, RHI 제출의 비용을 함께 줄입니다. 다음은 지오메트리의 비용으로, 모바일이나 나나이트를 쓰지 않는 메시, 스켈레탈 메시, 폴리지처럼 폴리곤 수가 직접 부담이 되는 대상부터 줄입니다.
텍스처의 메모리도 줄여야 합니다. 텍스처가 차지하는 GPU 메모리(VRAM)와 스트리밍 풀, 디스크의 크기, 압축 품질을 함께 봅니다. 반투명한 부분이 겹쳐 그려지는 오버드로와 그 픽셀 셰이더 비용도 줄이고, 그림자 맵과 실시간 광원, 루멘이나 라이트맵의 선택이 만드는 실행 중 비용과 메모리도 나눠 봅니다. 마지막으로 게임의 빌드 크기와 실행 중의 메모리 사용량도 줄입니다.
메시 최적화
가장 기본적이면서 중요한 최적화 대상은 3D 모델, 즉 메시입니다. 메시는 화면에 그릴 때의 비용과 물리로 충돌을 계산할 때의 비용을 함께 만듭니다. 그래서 아래에서는 그리는 비용을 줄이는 폴리곤 수와 LOD를 먼저 보고, 물리 비용을 줄이는 충돌 형상을 따로 봅니다.
폴리곤 수 줄이기
모바일과 나나이트를 쓰지 않는 메시, 스켈레탈 메시, 폴리지에서는 화면에 보이는 폴리곤의 수가 그대로 렌더링 부담이 될 수 있습니다. 반면 UE5에서 나나이트를 쓰는 환경이라면 폴리곤 수만 보지 말고, 화면의 픽셀 비용과 재질, 오버드로, 그림자의 비용을 함께 봐야 합니다.
폴리곤을 줄이는 방법은 세 가지입니다. 리토폴로지(retopology)는 Blender나 Maya, ZBrush 같은 외부 도구에서 모델의 폴리곤 배치를 처음부터 효율적으로 다시 만드는 것입니다. 데시메이션(decimation)은 폴리곤을 자동으로 줄이는 것으로, 스태틱 메시 에디터의 LOD 그룹이나 각 LOD의 Reduction Settings로 조정합니다. 원본 메시 자체를 고치는 일은 모델링 모드 같은 별도의 도구로 하는 것과 구분합니다. 너무 많이 줄이면 디테일이 사라지거나 모양이 깨지므로 눈으로 확인하며 알맞은 정도를 찾습니다. 마지막으로 필요 없는 면을 지우는데, 플레이와 그림자, 반사, 충돌 어디에도 필요 없는 면만 지웁니다. 벽의 뒷면을 지웠더니 빛이 새거나 반사가 빠지는 일이 생기지 않는지 확인합니다.
LOD
멀리 있는 물체는 가까이 있는 물체만큼 자세할 필요가 없습니다. LOD는 거리나 화면 크기에 따라 메시와 애니메이션, 재질의 비용을 낮추는 장치이며, 나나이트를 쓰는지와 플랫폼의 제약에 따라 효과와 설정 방법이 달라집니다.
LOD는 스태틱 메시 에디터의 디테일 패널에 있는 LOD Settings에서 정합니다. LOD 그룹 프리셋을 고르거나 LOD의 수를 정하고, 외부에서 만든 LOD를 가져오거나 각 LOD의 Reduction Settings를 조정한 뒤 Apply Changes로 자동으로 만든 결과를 적용합니다. 메뉴의 이름은 버전에 따라 다를 수 있습니다. 그다음 각 LOD의 Screen Size로, 메시가 화면에서 차지하는 크기에 따라 언제 바뀔지를 정합니다. 이것은 고정된 월드의 거리와는 다른 기준입니다. 뷰 모드의 LOD Coloration으로 각 LOD가 언제 쓰이는지 눈으로 확인합니다. 직접 만든 LOD가 자동으로 만든 LOD보다 품질이 좋고 효율적인 경우도 많습니다.
더 깊이: 나나이트는 LOD를 어떻게 대신하나
나나이트 문서에 따르면 나나이트 메시도 결국 삼각형 메시이지만, 임포트할 때 메시를 삼각형 묶음의 계층적인 클러스터(cluster)로 나누어 둡니다. 렌더링할 때는 카메라의 시점에 따라 이 클러스터를 서로 다른 상세도로 즉석에서 바꿔 끼우며, 같은 물체 안의 이웃 클러스터와는 틈 없이 이어집니다. 데이터는 필요할 때 스트리밍되어 보이는 디테일만 메모리에 올라오고, 나나이트는 기존의 그리기 호출을 거치지 않는 자체 렌더링 패스에서 그려집니다. 그래서 나나이트를 켠 스태틱 메시에는 앞에서 설정한 LOD가 필요 없고, 쓰는 폴리곤 수가 화면 해상도에 맞춰 저절로 조절됩니다.
문서는 삼각형이 많거나 화면에서 아주 작게 보이는 메시, 장면에 인스턴스가 많은 메시, 다른 나나이트 지오메트리를 크게 가리는 메시, 가상 섀도 맵으로 그림자를 드리우는 메시를 특히 좋은 후보로 꼽습니다. 반대로 하늘 구체처럼 삼각형이 화면에서 크고 아무것도 가리지 않으며 장면에 하나뿐인 메시는 예외에 가깝다고 설명합니다. 또 나나이트는 디스크에서 메시 데이터를 빠르게 스트리밍하는 데 기대므로 실행 환경에 SSD를 권장하며, 미리 굽는 조명을 쓰지 않는다면 임포트할 때 Generate Lightmap UVs를 꺼서 빌드 시간과 UV 데이터를 아끼라고 안내합니다.
충돌 형상
충돌 형상은 물리 시뮬레이션의 성능에 바로 영향을 줍니다. 물리 시뮬레이션 성능 조정에서 본 것처럼 단순 충돌을 적극적으로 씁니다. 스태틱 메시 에디터의 Collision 메뉴에서 상자, 구, 캡슐 모양의 단순 충돌을 더하거나, Auto Convex Collision으로 볼록 껍질 충돌을 만듭니다. 복잡 충돌은 정밀한 검사가 필요한 곳에만 골라 씁니다. Use Complex as Simple로 폴리곤 메시를 쓰는 바디는 물리 시뮬레이션을 할 수 없으므로, 움직이는 물리 물체에는 단순 충돌을 준비합니다.
텍스처 최적화
텍스처는 VRAM 사용량에 큰 영향을 줍니다. 텍스처 하나가 쓰는 메모리는 해상도와 압축 형식, 밉 맵이 함께 정하므로, 어느 하나만 바꾸고 결과를 판단하면 기대와 다른 숫자가 나오기 쉽습니다. 아래에서는 해상도를 조정하는 방법을 보고, 해상도에 따라 바이트 수가 어떻게 바뀌는지 직접 계산해 본 뒤, 압축 설정과 밉 맵을 차례로 살펴봅니다.
해상도 조정하기
텍스처의 해상도가 높을수록 VRAM을 더 많이 씁니다. 텍스처 에셋을 더블클릭해 디테일 패널에서 Compression Settings와 Max Texture Size를 조정합니다. Max Texture Size는 텍스처의 최대 해상도를 제한하는 값으로, 예를 들어 2048×2048 대신 1024×1024로 제한할 수 있습니다. 크기는 밉 맵과 스트리밍, 압축의 제약을 생각해 256, 512, 1024, 2048 같은 2의 거듭제곱을 기본으로 검토하되, UI처럼 2의 거듭제곱이 아닌 크기가 알맞은 용도도 있습니다. 멀리 있거나 중요하지 않은 물체의 텍스처에는 낮은 해상도를 씁니다.
메모리 계산해 보기
해상도를 줄이는 효과를 계산해 봅니다. 압축하지 않은 RGBA8 텍스처는 픽셀마다 4바이트를 쓰므로, 가장 큰 이미지 한 장만 계산하면 2048×2048은 2048 × 2048 × 4 = 16,777,216바이트, 즉 16 MiB이고, 1024×1024는 4 MiB입니다. 한 변을 절반으로 줄이면 픽셀 수가 1/4이 되므로 이 조건의 이미지 데이터도 1/4이 됩니다. 여기서 1 MiB = 1,048,576바이트이며, 10의 거듭제곱으로 세는 MB와는 구분합니다.
다만 이 계산은 압축 형식과 밉 맵, 스트리밍 상태, 메모리 정렬을 빼고 비교하기 위한 모델입니다. 실제 에셋에 표시되는 크기나 실제로 VRAM에 올라와 있는 양이 이 값과 같다고 단정하지 않습니다. 그래서 텍스처를 복사해 최대 크기만 바꿔 보고, 같은 카메라에서 표면의 글자와 노멀, 윤곽이 유지되는지와, 그 플랫폼의 메모리 통계를 각각 확인합니다.
압축 설정
플랫폼의 GPU 압축 형식과 알파 채널이 있는지에 따라 VRAM과 디스크의 크기, 품질이 달라집니다. 텍스처 에셋의 디테일 패널에서 Compression Settings를 고르면, 데스크톱에서는 BC 계열, 모바일에서는 ASTC나 ETC 계열처럼 대상 플랫폼에 맞는 형식이 쓰입니다.
설정은 용도에 맞춰 고릅니다. Normalmap은 노멀 맵에, VectorDisplacementmap은 벡터 변위 맵에, Masks는 AO와 거칠기, 금속성처럼 채널마다 독립된 값을 한 텍스처에 담은 마스크 텍스처에 씁니다. UserInterface2D (RGBA)는 압축 손실을 피해야 하는 UI 같은 곳에 검토하되 메모리 비용을 확인합니다. 알파가 있다는 이유만으로 이 형식을 꼭 써야 하는 것은 아닙니다. 쓰지 않는 알파 채널은 없애고, AO와 거칠기, 금속성 같은 흑백 맵 여러 장을 한 텍스처의 각 채널에 담으면 VRAM을 아낄 수 있습니다.
밉맵
밉 맵은 텍스처의 작은 사본들을 더 만들어 두므로 원본보다 데이터가 늘어납니다. 대신 거리에 따라 작은 사본을 써서 샘플링 품질과 텍스처 캐시의 효율, 스트리밍의 안정성을 높입니다. 대부분의 텍스처는 기본으로 밉 맵을 만들게 되어 있고, 텍스처 에셋의 Mip Gen Settings에서 확인합니다. UI 텍스처처럼 밉 맵이 필요 없는 경우에는 NoMipmaps로 정합니다. 밉 맵의 계산은 에셋 최적화 및 압축에서 다뤘습니다.
재질 최적화
복잡한 재질은 셰이더 계산의 부담을 늘립니다. 재질의 픽셀 셰이더는 그 재질이 덮은 화면의 픽셀마다 실행되므로, 같은 재질이라도 화면을 넓게 덮거나 여러 겹으로 그려질수록 비용이 커집니다. 그래서 아래에서는 픽셀 하나의 계산을 줄이는 셰이더 복잡도와, 같은 픽셀을 여러 번 그리게 만드는 오버드로를 나눠 봅니다.
셰이더 복잡도 줄이기
재질의 비용은 노드의 수보다 셰이더 명령의 수와 텍스처를 읽는 횟수, 분기, 셰이더 조합(permutation)의 수, 오버드로에 더 크게 좌우됩니다. 먼저 Shader Complexity 뷰 모드로 비용이 클 것으로 보이는 영역을 찾고, 그 결과를 실제 GPU 시간과 맞춰 봅니다.
줄이는 방법은 여러 가지입니다. 필요 없는 노드를 지우고, 계산이 많은 노드 대신 간단한 수학 계산을 씁니다. 정적 스위치 파라미터(Static Switch Parameter)로 쓰지 않는 분기를 컴파일할 때 없애되, 조합이 늘어 셰이더의 가짓수가 늘어나는지 확인합니다. 여러 재질에 겹치는 로직은 재질 함수(Material Function)로 관리하는데, 함수로 묶는다고 실행 중의 계산이 공유되거나 줄어드는 것은 아닙니다. 마스터 재질과 재질 인스턴스 방식으로 스칼라와 벡터 파라미터를 조절하며, 정적 스위치를 바꾸면 새 셰이더 조합을 컴파일해야 할 수 있습니다. 조명의 반응이 필요 없는 배경이나 UI, 특수 효과에는 조명 계산을 빼는 Unlit을 검토할 수 있지만, 조명 정보가 필요한 표면까지 무리하게 바꾸면 품질 기준이 흔들립니다.
오버드로 줄이기
유리나 물, 파티클, 나뭇잎처럼 투명하거나 반투명한 재질은 겹칠수록 같은 픽셀을 여러 번 그려 GPU 부담을 크게 늘립니다. Shader Complexity & Quads나 Quad Overdraw로 오버드로가 심한 곳을 찾습니다.
블렌드 모드는 되도록 불투명(Opaque)을 먼저 쓰고, 반투명(Translucent)이 꼭 필요한 곳에서는 면적과 겹침을 줄입니다. 마스크드(Masked)는 정렬하고 섞는 부담을 낮출 수 있지만 알파 검사와 픽셀 비용은 남으므로, Shader Complexity와 Quad Overdraw로 확인합니다. 한 화면에 보이는 반투명 물체의 수를 줄이고, 파티클은 수와 크기, 수명, 텍스처의 오버드로를 줄입니다.
레벨 최적화
레벨을 구성하는 방식도 성능에 큰 영향을 줍니다. 에셋 하나하나가 가벼워도, 레벨에 무엇을 몇 개 어떻게 놓았는지에 따라 CPU가 제출할 그리기 호출의 수와 GPU가 실제로 그릴 물체의 수가 달라지기 때문입니다. 아래에서는 그리기 호출을 줄이는 액터 병합, 보이지 않는 물체를 빼는 컬링, 레벨 전체의 조명 방식을 차례로 봅니다.
액터 병합
작은 스태틱 메시 액터 여러 개를 하나로 합치면 렌더링 명령을 제출하는 비용을 줄일 수 있습니다. 병합할 액터를 모두 선택한 뒤 레벨 에디터 위쪽 메뉴의 Actor에서 Merge Actors를 골라 방식과 설정을 정합니다. 예전 버전에서는 이 도구가 Window 메뉴의 Developer Tools에 있었습니다. 벽과 창문, 기둥 같은 건축 부품이나, 돌무더기, 나뭇가지 묶음 같은 작은 물체 무리에 효과적입니다.
다만 이득이 늘 생기는 것은 아닙니다. 재질 섹션의 수가 그대로 남아 있거나, 합친 덩어리가 커져 가림 판정과 LOD의 단위가 커지면 이득이 줄어들 수 있으므로, 합치기 전후를 비교합니다. 또 하나로 합친 원래의 부품은 따로 움직일 수 없습니다. 합친 결과에도 LOD를 만들 수 있지만 원래 액터마다의 컬링과 LOD 단위는 달라지므로, 따로 움직여야 할 부품은 빼 둡니다.
컬링
컬링은 화면에 보이지 않는 물체를 그리지 않게 해 비용을 아낍니다. 시야 절두체 컬링(frustum culling)은 카메라 시야 밖의 물체를 그 시야에서 뺍니다. 다만 그림자나 반사처럼 다른 단계에는 그 물체가 필요할 수 있습니다. 오클루전 컬링(occlusion culling)은 다른 물체에 가려진 물체를 바운드와 가시성 결과에 따라 뺍니다. 판정에도 비용과 지연이 있고 렌더링 경로마다 차이가 있으므로, 가려지는 즉시 완벽하게 빠진다고 가정하지 않습니다.
거리 컬링(distance culling)은 정해진 거리 밖의 물체를 아예 그리지 않게 합니다. 스태틱 메시 컴포넌트의 Rendering 항목에서 Min Draw Distance, Desired Max Draw Distance, Current Max Draw Distance를 확인하거나, 여러 액터에는 컬 디스턴스 볼륨을 씁니다. 여기서 LOD와 컬링을 구분합니다. LOD는 보이는 품질을 단계적으로 낮추는 장치이고, 물체를 완전히 빼는 것은 거리 컬링이나 컬 디스턴스 볼륨으로 관리합니다.
조명 방식
조명 방식에 따라 비용의 성격이 다릅니다. Static 조명은 빌드한 뒤 실행 중의 조명 비용은 낮지만, 라이트맵의 메모리와 빌드 시간, 실행 중에 바뀌지 못하는 제약이 생깁니다. Stationary 조명은 정적인 요소와 동적인 요소를 섞을 수 있지만, 겹치는 수의 제한과 실시간 그림자의 비용을 함께 관리해야 합니다. Movable 조명과 루멘은 실시간 변화와 동적인 간접광이 필요할 때 알맞지만, 그림자와 반사의 비용을 ProfileGPU로 확인해야 합니다. 루멘 중심의 프로젝트라면 미리 계산한 정적 조명을 쓰는지도 프로젝트 설정과 함께 봅니다.
바꾼 뒤에 비교할 것
최적화는 분석하고, 적용하고, 다시 측정하고, 다시 분석하는 일의 반복입니다. 한 번에 너무 많은 것을 바꾸지 말고, Stat Unit과 ProfileGPU, Shader Complexity, Quad Overdraw, 텍스처 스트리밍 통계에서 어떤 값이 움직였는지 기록합니다.
바꾼 뒤에는 같은 카메라에서 바꾸기 전과 후의 프레임 시간과 메모리를 기록합니다. LOD가 바뀌는 순간과 압축 손실, 빛샘, 충돌, 그리고 액터를 따로 조작하는 기능이 유지되는지도 확인합니다. 개선 폭이 작거나 품질이 기준을 벗어났다면, 바꾼 것을 되돌리고 다음 비용 후보를 검토합니다. 큰 월드라면 월드 파티션이나 레벨 스트리밍이 불러오는 범위도 함께 확인합니다. LOD는 더 단순한 메시로 바꿔 그리고 컬링은 그리지 않게 할 뿐, 둘 다 데이터를 메모리에서 내리지는 않으므로, 실제로 데이터를 내리는 일과는 서로 다른 동작입니다.
정리
- 에셋과 레벨의 최적화는 렌더링 제출, 지오메트리, 텍스처 메모리, 오버드로, 그림자와 조명, 데이터 크기의 비용을 나눠 줄이는 일입니다.
- 폴리곤 수는 모바일과 나나이트가 아닌 메시에서 특히 중요하며, LOD는
Screen Size라는 화면 크기 기준으로 바뀌고, 움직이는 물체에는 단순 충돌을 씁니다. - RGBA8 텍스처의 가장 큰 이미지는 한 변을 절반으로 줄이면 1/4이 되지만, 실제 VRAM과 파일 크기는 압축과 밉 맵, 플랫폼 형식에 따라 따로 확인합니다.
- 압축 설정은 용도에 맞춰 고르고, 흑백 맵은 채널에 묶어 아끼며, 밉 맵은 데이터를 늘리지만 샘플링과 스트리밍을 안정시킵니다.
- 재질의 비용은 노드 수보다 명령과 텍스처 읽기, 조합 수, 오버드로가 정하며, 반투명은 면적과 겹침을 줄입니다.
- 액터 병합은 그리기 비용을 줄이지만 컬링과 LOD 단위가 커지는 대가가 있으며, LOD와 컬링, 데이터를 내리는 스트리밍은 서로 다른 동작입니다.
연습 문제
1. 1024를 512로 줄이면
앞과 같은 조건, 즉 압축하지 않은 RGBA8과 가장 큰 이미지 한 장의 조건에서 1024×1024 텍스처를 512×512로 줄였습니다. 이미지 데이터는 몇 MiB가 되고 몇 퍼센트 줄었을까요? 이 감소율이 원본 파일과 패키지의 크기에도 그대로 적용된다고 말할 수 있을까요?
해설 보기
512 × 512 × 4 = 1,048,576바이트, 즉 1 MiB입니다. 4 MiB에서 1 MiB로 3 MiB가 줄었으므로 감소율은 75%입니다.
하지만 원본 파일의 압축, 쿠킹한 결과, 밉 맵과 플랫폼의 압축 형식은 조건이 다르므로, 파일과 패키지의 크기는 따로 확인해야 합니다. 가장 큰 이미지 한 장으로 한 계산을 모든 저장 공간의 측정값처럼 쓰지 않습니다.
2. 합쳤더니 오히려 느려졌다
넓은 마을의 집 수십 채를 모두 한 메시로 합쳤더니, 그리기 호출은 줄었는데 마을 한쪽 끝에서 다른 쪽을 바라보지 않을 때도 GPU 시간이 늘었습니다. 왜 그럴 수 있을까요?
해설 보기
집들을 하나로 합치면 컬링과 LOD의 단위가 마을 전체 크기로 커집니다. 그러면 그 덩어리의 일부만 보여도 전체를 그려야 하고, 가려진 집이나 카메라 뒤의 집도 따로 뺄 수 없게 됩니다. 멀리 있는 집도 가까운 집과 같은 LOD로 그려질 수 있습니다.
그래서 너무 넓은 범위를 하나로 합치지 말고, 가까이 모여 있고 함께 보이는 부품끼리 작은 단위로 합친 뒤, 합치기 전후의 그리기 호출과 GPU 시간을 함께 비교합니다.
3. 멀리 있는 나무를 없애고 싶다
멀리 있는 작은 나무들을 아예 그리지 않게 하려고 LOD를 하나 더 만들어 폴리곤을 크게 줄였습니다. 그런데 나무는 여전히 멀리서도 그려지고 있습니다. 무엇을 써야 할까요?
해설 보기
LOD는 보이는 품질을 단계적으로 낮추는 장치일 뿐, 물체를 그리지 않게 하지는 않습니다. 가장 낮은 LOD로 바뀌어도 나무는 계속 그려집니다.
일정 거리 밖에서 아예 그리지 않게 하려면 거리 컬링을 씁니다. 나무 메시 컴포넌트의 Desired Max Draw Distance를 정하거나, 여러 나무에 한꺼번에 적용하려면 컬 디스턴스 볼륨을 놓습니다. 그다음 같은 카메라에서 나무가 사라지는 거리가 눈에 거슬리지 않는지 확인합니다.
액터 병합의 설정은 Epic의 액터 병합 안내에서 확인할 수 있습니다.
다음 글 환경 설정의 성능 영향 분석에서는 조명과 안개, 후처리 같은 환경 설정 하나하나가 프레임에 주는 영향을 비교하고, 품질과 비용 사이에서 무엇을 남길지 판단하는 방법을 다룹니다.