본문 바로가기

에셋 최적화 및 압축

텍스처·메시·머티리얼·오디오·비디오 에셋의 용량과 런타임 비용을 줄여 로딩·메모리·프레임을 개선합니다.

에셋을 잘 가져오고 정리하는 것만으로는 충분하지 않습니다. 같은 장면이라도 에셋을 얼마나 알맞은 크기와 형식으로 만들었느냐에 따라, 빌드 용량과 로딩 시간, 실행 중의 프레임이 크게 달라집니다. 최적화가 부족하면 빌드가 커지고, 로딩이 길어지고, 프레임이 불안정해집니다.

이 글에서는 텍스처, 3D 모델, 재질, 사운드와 비디오 에셋을 차례로 살펴보며, 각 에셋에서 용량과 실행 비용을 줄이는 설정이 무엇인지 정리합니다. 에셋마다 만드는 병목이 다르므로, 하나의 수치로 판단하지 않고 종류별로 확인하는 것이 핵심입니다.


최적화하지 않은 에셋이 만드는 문제

최적화하지 않은 에셋은 네 가지 문제를 일으킵니다. 첫째, 성능이 떨어집니다. 지나치게 많은 폴리곤과 고해상도 텍스처, 계산이 많은 재질은 GPU와 CPU에 부담을 주어 프레임 속도를 떨어뜨립니다. 둘째, 빌드 용량이 늘어납니다. 필요 없는 데이터가 들어 있는 에셋은 최종 게임 파일을 키워 다운로드 시간과 설치 공간을 늘립니다.

셋째, 메모리를 많이 씁니다. 실행 중에 더 많은 메모리를 차지하므로, 특히 저사양 기기에서 문제가 됩니다. 넷째, 로딩이 길어집니다. 에셋이 클수록 읽어 들이는 데 시간이 더 걸리기 때문입니다. 이 네 가지는 서로 연결되어 있지만 원인이 같지는 않아서, 어떤 문제를 줄이려는지에 따라 손볼 에셋과 설정이 달라집니다.


텍스처: 해상도, 압축, 밉 맵

텍스처는 화면의 품질에 큰 영향을 주는 동시에, 메모리를 가장 많이 차지하는 에셋 가운데 하나입니다. 텍스처 하나의 비용은 세 가지 설정이 함께 정합니다. 해상도는 저장할 픽셀 수를, 압축 설정은 픽셀을 어떤 형식으로 줄여 담고 엔진이 그 값을 어떻게 읽을지를, 밉 맵은 화면에서 작게 보일 때 쓸 작은 사본을 만들지를 정합니다. 아래에서는 이 순서로 살펴본 뒤, 해상도와 밉 맵이 용량에 주는 영향을 계산으로 비교합니다.

알맞은 해상도 고르기

모든 텍스처가 최고 해상도일 필요는 없습니다. 캐릭터의 얼굴이나 주요 배경처럼 화면에 크게 나오거나 세부가 중요한 텍스처에만 2048×2048이나 4096×4096 같은 고해상도를 쓰고, 멀리 있거나 작은 오브젝트에는 512×512나 256×256 같은 낮은 해상도를 씁니다.

크기는 256, 512, 1024처럼 2의 거듭제곱(power of two)을 출발점으로 삼습니다. 밉 맵을 만들고 스트리밍하는 일반적인 텍스처에서 이 크기가 다루기 쉽기 때문입니다. 2의 거듭제곱이 아닌 크기도 용도와 설정에 따라 쓸 수 있지만, 그 경우에는 밉 맵과 압축, 스트리밍을 지원하는 조건을 확인합니다.

에디터 안에서도 해상도를 제한할 수 있습니다. 콘텐츠 브라우저에서 텍스처를 더블클릭해 텍스처 에디터를 열고, 디테일 패널에서 Maximum Texture Size를 정하면 엔진이 만드는 텍스처의 최대 크기가 그 값으로 제한됩니다. 이것은 외부 원본 파일을 줄이는 일과 다르며, 원본은 그대로 둔 채 엔진 쪽 결과만 작게 만듭니다. 값을 바꾼 뒤에는 텍스처 에디터에 표시되는 실제 게임 내 크기(Max In-Game)와 밉 개수, 대상 플랫폼 설정을 함께 확인합니다.

압축 설정은 저장한 정보에 맞춰 고르기

텍스처 에디터의 디테일 패널에 있는 Compression Settings는 텍스처를 어떤 형식으로 압축할지 정합니다. 이 설정은 용량과 메모리를 줄이는 동시에, 엔진이 텍스처의 값을 어떻게 읽을지도 정합니다.

TEXTURE DATA · COMPRESSION

텍스처의 의미에 맞춰 압축과 sRGB를 선택합니다

표시할 색과 선형 수치 데이터는 읽는 방식이 다릅니다. 아래 프리셋을 출발점으로 재질의 샘플러·대상 플랫폼·메모리 비용을 확인합니다.

설정과 적용 조건
정보설정확인할 조건
색상Default · 보통 sRGB 사용표시할 색을 저장한 텍스처인지 확인합니다.
법선Normalmap노멀 방향 규약과 녹색 채널 반전 필요 여부를 확인합니다.
마스크Masks · sRGB 끔거칠기·금속성 등 채널별 수치를 선형 데이터로 읽습니다.
UIUserInterface2D알파·선명도와 메모리 비용을 함께 확인합니다.
벡터 변위VectorDisplacementmap벡터 변위를 읽는 재질과 데이터 형식에 맞춥니다.
색상

Default · 보통 sRGB 사용

표시할 색을 저장한 텍스처인지 확인합니다.

법선

Normalmap

노멀 방향 규약과 녹색 채널 반전 필요 여부를 확인합니다.

마스크

Masks · sRGB 끔

거칠기·금속성 등 채널별 수치를 선형 데이터로 읽습니다.

UI

UserInterface2D

알파·선명도와 메모리 비용을 함께 확인합니다.

벡터 변위

VectorDisplacementmap

벡터 변위를 읽는 재질과 데이터 형식에 맞춥니다.

위 그림은 텍스처에 저장한 정보의 종류에 따라 고를 압축 설정과, 그때 확인할 조건을 표로 보여 줍니다. 핵심은 화면에 표시할 색과 재질 계산에 쓰는 수치는 읽는 방식이 다르다는 점입니다. 색을 담은 텍스처는 Default를 고르고 보통 sRGB를 켭니다. sRGB는 사람의 눈에 맞춰 밝기를 보정해 저장한 색 공간이라, 엔진이 읽을 때 이 보정을 되돌려야 올바른 색이 나옵니다.

표의 나머지 행은 색이 아닌 정보입니다. 표면의 방향을 담은 노멀 맵은 Normalmap을 고르고, 외부 도구와 엔진의 노멀 방향 규칙이 달라 녹색 채널을 뒤집어야 하는지 확인합니다. 거칠기나 금속성처럼 채널마다 수치를 담은 마스크는 Masks를 고르고 sRGB를 꺼서, 값을 보정 없이 그대로 읽게 합니다. UI 텍스처는 UserInterface2D를 고르는데, 이 설정은 선명도를 지키려고 압축을 거의 하지 않아 메모리를 많이 쓰므로 알파와 선명도, 메모리 비용을 함께 봅니다. 그리고 벡터 변위 맵은 VectorDisplacementmap을 골라 그 값을 읽는 재질과 데이터 형식에 맞춥니다. 그림의 설명처럼 이 프리셋은 출발점일 뿐이고, 재질의 샘플러 설정과 대상 플랫폼, 메모리 비용을 함께 확인해야 합니다.

그래서 색을 담은 나무 텍스처와 거칠기 마스크를 같은 프리셋으로 한꺼번에 바꾸면 안 됩니다. 앞의 것은 표시할 색이고 뒤의 것은 재질이 계산에 쓸 수치이므로, 표에서 서로 다른 행을 골라야 합니다. 마스크에 sRGB가 켜져 있으면 0.5로 칠한 거칠기가 다른 값으로 읽혀, 표면이 의도보다 더 반짝이거나 더 거칠게 보입니다. 설정을 바꾼 뒤에는 재질에서 색과 거칠기가 어떻게 보이는지와, 텍스처 에디터에 표시되는 크기를 함께 비교합니다.

밉 맵

밉 맵(mip map)은 텍스처를 절반씩 줄인 여러 해상도의 사본을 미리 만들어 두고, 화면에서 텍스처가 차지하는 크기와 샘플링 범위에 맞는 해상도를 골라 쓰는 기술입니다. 멀리 있어 작게 보이는 물체에 원본 해상도를 그대로 쓰면, GPU가 필요 이상으로 많은 텍셀을 읽어야 하고 화면에는 간섭 무늬(모아레)와 깜빡임이 생깁니다. 밉 맵은 작은 사본을 쓰게 해 샘플링 비용을 줄이고 이런 무늬도 줄여 줍니다.

다만 밉 맵에도 저장 비용이 있습니다. 한 단계 작은 사본은 원본 픽셀 수의 1/4이므로, 모든 단계를 더하면 1/4 + 1/16 + 1/64 + … 로 원본의 약 1/3을 더 씁니다. 텍스처 에디터의 Mip Gen Settings에서 밉 맵을 어떻게 만들지 정하며, 대부분의 텍스처는 기본으로 밉 맵을 만들게 되어 있습니다. UI 텍스처처럼 늘 원본 해상도로 써야 하는 특수한 경우에는 NoMipmaps를 고를 수 있지만, 그 텍스처가 작게 보일 때의 샘플링 비용이 생길 수 있습니다.

계산으로 비교해 보기

같은 채널 수와 압축 형식이라고 가정하고, 기본 해상도를 4096×4096에서 2048×2048로 낮추면 어떻게 될까요? 가로와 세로가 각각 절반이 되므로 기본 이미지의 픽셀 수는 전체의 1/4이 됩니다. 다만 실제로 몇 바이트를 쓰는지는 압축 블록과 밉 맵, 그 텍스처가 메모리에 올라와 있는지까지 함께 확인해야 합니다.

반대로 4096 이미지를 그대로 둔 채 밉 맵만 만드는 것은 기본 이미지를 지우는 일이 아닙니다. 작은 사본들을 더하는 일이므로, 앞에서 계산했듯이 저장할 데이터는 오히려 약 1/3 늘어납니다. 밉 맵이 줄이는 것은 용량이 아니라 멀리 있는 물체를 그릴 때의 샘플링 비용입니다.

더 깊이: 압축 형식이 정하는 실제 메모리

압축 설정은 GPU가 그대로 읽을 수 있는 블록 압축(block compression) 형식으로 이어집니다. 언리얼 엔진 4.27의 압축 설정 문서는 Default가 알파 채널이 없으면 DXT1(BC1)로, 있으면 DXT5(BC3)로 압축되고 Normalmap은 BC5로 압축된다고 정리하며, 5.8의 API 설명도 Masks가 Default와 같은 압축을 쓴다고 적고 있습니다. DXT 계열은 픽셀을 4×4 블록으로 묶어 압축하는데, 텍스처 형식 문서에 따르면 압축 비율이 DXT1은 8:1, DXT5는 4:1로 일정합니다. 그래서 해상도가 같아도 알파 채널 때문에 DXT5로 압축되면 메모리가 DXT1의 약 두 배가 됩니다.

같은 문서의 표는 밉 맵을 모두 포함한 크기를 보여 줍니다. 2048×2048은 DXT1에서 2.66MB, DXT5에서 5.33MB이고, 4096×4096은 각각 10.6MB와 21.3MB입니다. 앞에서 해상도를 4096에서 2048로 낮추면 기본 이미지의 픽셀 수가 1/4이 된다고 계산했는데, 밉 맵까지 포함한 실제 크기도 같은 비율로 줄어든다는 것을 이 표로 확인할 수 있습니다.

압축하지 않는 설정은 이보다 훨씬 큽니다. 4.27 문서에서 UserInterface2D는 압축하지 않은 B8G8R8A8 형식이므로 픽셀마다 4바이트를 쓰고, 이는 픽셀당 0.5바이트인 DXT1의 8배입니다. 또 5.8의 API 설명에 따르면 Grayscale에 sRGB를 켜면 압축하지 않은 RGBA8이 되므로, 흑백 마스크라면 BC4로 압축하는 설정을 먼저 검토합니다.


3D 모델: 폴리곤, LOD, UV

3D 모델의 폴리곤(polygon) 수는 렌더링 성능에 바로 영향을 줍니다. 폴리곤이 많을수록 GPU가 위치를 계산할 버텍스와 화면에 그릴 삼각형이 늘어나기 때문입니다. 비용을 줄이는 방법은 모델 자체를 가볍게 만드는 것과, 멀리 있을 때만 가벼운 모델로 바꿔 그리는 LOD 두 가지입니다. 이어서 다루는 UV는 폴리곤 수와는 관계가 없지만, 텍스처 해상도를 얼마나 낭비 없이 쓰는지와 구운 조명의 품질을 정합니다.

필요한 만큼만 폴리곤 쓰기

화면에 크게 나오지 않거나 중요하지 않은 오브젝트는 폴리곤 수를 줄입니다. 폴리곤이 너무 많으면 GPU가 쓸데없이 많은 일을 하게 됩니다. 가장 좋은 방법은 Blender나 3ds Max, Maya 같은 외부 모델링 도구에서 내보내기 전에 폴리곤 수를 다듬는 것이지만, 언리얼의 스태틱 메시 에디터에서도 LOD의 리덕션 설정(Reduction Settings)으로 폴리곤 수를 줄일 수 있습니다.

LOD 설정하기

레벨 오브 디테일(Level of Detail, LOD)은 메시가 화면에서 차지하는 크기에 따라 상세도가 다른 메시를 골라 쓰는 기능입니다. 가까이 있어 크게 보일 때는 품질이 높은 메시를, 멀리 있어 작게 보일 때는 품질이 낮은 메시를 그려 비용을 줄입니다. 이 설명은 일반 스태틱 메시의 LOD에 해당하며, 나나이트(Nanite)를 켠 메시는 가상화된 지오메트리 방식으로 상세도를 따로 정합니다.

LOD는 스태틱 메시 에디터의 디테일 패널에 있는 LOD Settings에서 정합니다. Number of LODs로 LOD 단계 수를 정하는데, 예를 들어 3이면 LOD 0, 1, 2의 세 단계이고 원본 메시가 LOD 0입니다. 그다음 LOD 그룹을 고르거나 각 단계의 Reduction Settings를 정하고 Apply Changes로 적용합니다. 실제로 보이는 옵션은 엔진 버전과 메시 설정에 따라 다를 수 있습니다.

각 LOD 단계에는 화면 크기(Screen Size) 값이 있어, 그 LOD를 쓰기 시작할 화면상의 크기를 정규화한 값으로 정합니다. 화면 크기 값을 작게 정할수록 메시가 더 작게 보일 때, 즉 더 멀리 있을 때 그 낮은 LOD로 바뀝니다. LOD는 특히 배경 오브젝트나 여러 번 반복해 배치하는 오브젝트에서 효과가 큽니다. 한 번 줄인 비용이 배치한 수만큼 곱해지기 때문입니다.

UV를 용도에 맞게 나누기

UV는 3D 표면의 각 점이 2D 텍스처의 어느 위치를 쓸지 정하는 좌표입니다. 색 텍스처용 UV는 같은 무늬를 반복하려고 일부러 겹치거나 타일링해도 됩니다. 반면 조명을 미리 구워 저장하는 라이트맵(lightmap)용 UV는 표면마다 고유한 조명을 저장해야 하므로, 별도의 UV 채널에 서로 겹치지 않게 펼치고 조각 사이에 충분한 간격을 둬야 합니다. 어느 쪽이든 UV 공간을 빈틈없이 써야 텍스처 해상도를 낭비하지 않습니다.


재질: 인스턴스와 계산량

재질은 오브젝트의 표면 속성을 정하며, 복잡한 재질은 렌더링 성능에 큰 영향을 줍니다.

가장 먼저 쓸 방법은 재질 인스턴스(Material Instance)입니다. 기준이 되는 마스터 재질(Master Material)을 하나 만들고, 그것에서 파생한 재질 인스턴스로 색과 텍스처, 수치만 바꿔 씁니다. 스칼라나 벡터 같은 일반 파라미터는 인스턴스에서 바꿔도 셰이더를 다시 컴파일하지 않으므로 값을 바꿔 보며 작업하기 좋습니다. 다만 정적 스위치(Static Switch) 같은 파라미터는 별도의 셰이더 변형을 만들 수 있고, 인스턴스를 쓴다고 그리기 호출(draw call)이 저절로 줄어드는 것도 아닙니다.

다음으로 실제 출력에 연결된 계산을 점검합니다. 최종 출력까지 이어진 비싼 연산과 텍스처 샘플이 비용을 만드는 것이므로, 그쪽을 줄여야 효과가 있습니다. 연결되지 않은 노드를 지우면 그래프를 읽기는 쉬워지지만, 그것만으로 실행되는 셰이더가 빨라진다고 볼 수는 없습니다. 복잡한 재질은 재질 함수(Material Function)나 그룹으로 노드를 묶어 읽기 쉽고 다시 쓰기 쉽게 만듭니다.

모바일 같은 저사양 플랫폼이 목표라면 재질의 셰이더 명령(shader instruction) 수를 줄이는 데 신경 써야 합니다. 명령 수는 재질 에디터의 통계(Stats) 패널에서 확인합니다.


사운드와 비디오

사운드는 편집용 원본의 품질을 그대로 보존해 두고, 사운드 웨이브(Sound Wave) 에셋의 압축 품질과 스트리밍, 대상 플랫폼의 코덱을 점검합니다. 외부 파일을 OGG로 바꾼다고 게임 안에서의 메모리나 디코딩 비용이 정해지지는 않습니다. 엔진은 가져온 원본을 다시 압축해 쓰기 때문입니다.

사운드 감쇠(attenuation)는 소리가 들리는 범위와 거리에 따라 볼륨이 줄어드는 방식을 정해 공간감을 만듭니다. 멀리 있는 소리를 작게 하거나 들리지 않게 할 수 있지만, 들리지 않는 음원도 처리 비용이 남을 수 있으므로 가상화와 동시 재생 수 제한 같은 설정도 함께 확인합니다.

게임 안에서 재생할 비디오는 먼저 대상 플랫폼과 미디어 플레이어(Media Player) 플러그인이 지원하는 코덱과 컨테이너를 확인합니다. 지원되는 형식 안에서 해상도와 비트레이트, 디코딩 비용을 비교해 고릅니다.


정리

  • 최적화하지 않은 에셋은 성능 저하, 빌드 용량 증가, 메모리 증가, 로딩 지연을 일으키며, 에셋마다 만드는 병목이 다릅니다.
  • 텍스처는 용도에 맞는 해상도를 고르고, Maximum Texture Size로 원본을 둔 채 엔진 쪽 크기를 제한할 수 있습니다.
  • 압축 설정은 저장한 정보에 맞춰, 색은 Default와 sRGB, 노멀 맵은 Normalmap, 마스크는 Masks와 sRGB 끔, UI는 UserInterface2D를 출발점으로 고릅니다.
  • 해상도를 가로와 세로 모두 절반으로 줄이면 픽셀 수는 1/4이 되고, 밉 맵은 용량을 약 1/3 늘리는 대신 멀리 있는 물체의 샘플링 비용을 줄입니다.
  • 스태틱 메시는 LOD의 단계 수와 화면 크기로 멀리 있을 때의 비용을 줄이며, 라이트맵 UV는 겹치지 않는 별도 채널에 둡니다.
  • 재질은 인스턴스로 값만 바꿔 쓰고, 출력에 연결된 계산과 셰이더 명령 수를 줄이며, 사운드와 비디오는 엔진과 플랫폼의 압축, 코덱 설정을 확인합니다.

연습 문제

1. 해상도를 줄이면 얼마나 줄어들까

같은 압축 형식의 텍스처 해상도를 2048×2048에서 512×512로 낮췄습니다. 기본 이미지의 픽셀 수는 원래의 몇 분의 몇이 될까요? 이 비율만으로 실제 메모리 사용량을 단정할 수 있을까요?

해설 보기

가로와 세로가 각각 1/4이 되므로 픽셀 수는 1/4 × 1/4 = 1/16이 됩니다.

하지만 실제 메모리 사용량은 압축 블록의 크기, 밉 맵의 유무, 그 텍스처가 지금 메모리에 올라와 있는지에 따라 달라지므로 이 비율만으로 단정할 수 없습니다. 텍스처 에디터에 표시되는 크기와 실제 메모리 측정으로 확인합니다.

2. 거칠기 마스크가 이상하게 보인다

거칠기 값을 담은 마스크 텍스처를 가져왔더니, 0.5로 칠한 부분이 재질에서 기대와 다른 거칠기로 보입니다. 텍스처 설정을 보니 Compression Settings가 Default이고 sRGB가 켜져 있습니다. 무엇을 바꿔야 할까요?

해설 보기

거칠기는 표시할 색이 아니라 재질이 계산에 쓰는 수치이므로, 밝기 보정을 거치지 않고 그대로 읽어야 합니다. sRGB가 켜져 있으면 엔진이 색처럼 보정을 되돌리며 읽어서 값이 달라집니다.

Compression Settings를 Masks로 바꾸고 sRGB를 끈 뒤, 재질에서 거칠기가 의도대로 보이는지 다시 확인합니다. 재질의 텍스처 샘플러 유형도 마스크에 맞는지 함께 봅니다.

3. 같은 바위가 수백 개 있는 장면

같은 바위 메시를 수백 개 배치한 장면에서 프레임이 떨어집니다. 바위 하나는 폴리곤이 많고, 대부분은 카메라에서 멀리 있습니다. 이 메시에서 무엇을 먼저 조정해 볼 수 있을까요?

해설 보기

대부분의 바위가 멀리 있어 작게 보이므로, LOD를 설정해 화면 크기가 작을 때는 폴리곤이 적은 메시가 쓰이게 합니다. 같은 메시를 여러 번 배치했으므로 한 번 줄인 비용이 배치 수만큼 곱해져 효과가 큽니다.

Number of LODs와 각 단계의 Reduction Settings, 화면 크기 값을 정하고 Apply Changes로 적용한 뒤, 같은 카메라 위치에서 프레임 시간을 다시 비교합니다. 프로젝트가 나나이트를 쓴다면 이 메시에 나나이트를 켜는 방법도 함께 검토할 수 있습니다.

압축 설정에 들어갈 수 있는 값의 목록은 Epic의 TextureCompressionSettings 레퍼런스에서 확인할 수 있습니다.

다음 글 에셋을 사용하는 레벨 디자인 기초에서는 가져와 정리한 에셋을 레벨에 배치하고, 폴더와 그룹, 붙이기로 배치한 액터를 정리하는 방법을 살펴봅니다.