디지털 프로젝트의 메모리 사용량에 있어 큰 부분을 차지하는 것 중 하나는 사용되는 텍스처의 크기와 양입니다. 다행히 언리얼 엔진에는 모든 프로젝트에 걸쳐 사용되는 텍스처 크기를 손실 없이 줄일 수 있는 매우 탄탄한 시스템이 있습니다. 아래에서는 이 시스템을 살펴보고 이를 통해 프로젝트에 필요한 텍스처 메모리를 줄이는 법을 알아보겠습니다.
텍스처 해상도
언리얼 엔진은 최소 1x1에서 .ini 파일을 약간 수정하면 텍스처 해상도를 최대 8192x8192까지 지원합니다. 현재 DirectX 비디오 어댑터와 게임 콘솔은 1x1에서 2048x2048, 최대 8192x8192까지 다양한 텍스처 해상도를 지원하고 있습니다. 특정 하드웨어 장치가 지원하는 최고 텍스처 해상도는 제조 업체, 모델, 가용 텍스처 메모리에 따라 다릅니다. 언리얼 엔진 4에는 월드 지오메트리나 유저 인터페이스와 같은 다양한 영역에 렌더링되는 텍스처 해상도 관리용 기능 및 설정이 다수 있습니다.
엔진 텍스처 해상도 한계
언리얼 엔진 4는 기본적으로 텍스처 밉의 최대 수를 14로 제한합니다. 따라서 렌더링 가능한 최대 텍스처의 크기는 사실상 8192으로 제한됩니다(1x1에서 8192x8192까지가 14밉입니다). 여기에는 8192 텍스처를 임포트해도 4096의 mip1까지만 렌더링된다는 부작용이 있습니다. 엔진의 소스 파일에서 13으로 기본 설정된 상수 값 MAX_TEXTURE_MIP_COUNT는 8192텍스처 렌더링을 지원할 수 있도록 14로 변경 가능합니다. 이 상수는 다음의 소스 파일에 정의되어 있습니다(QAMar09 기준으로, 다른 QA 버전의 경우 확인이 필요합니다).
Src\D3D10Drv\Src\D3D10Device.cpp
Src\Engine\Inc\RHI.h
Src\Engine\Inc\UnTex.h
Src\Engine\Src\RHI.cpp
Src\Engine\Src\TextureCube.cpp
UE 4.8 이후 프로젝트의 BaseDeviceProfiles.ini 파일에 다음 텍스트를 추가하고 MaxLODsize 를 8192 로 설정해 주면 C++ 코드 변경 없이도 8192까지의 텍스처를 사용할 수 있습니다.
[/Script/Engine.TextureLODSettings]
TextureLODGroup_World=(MinLODSize=1,MaxLODSize=8192,LODBias=0,MinMagFilter=aniso,MipFilter=point)
크기를 늘리고자 하는 섹션을 추가한 뒤 파일을 저장하고 에디터를 재시작합니다. 에디터 재시작 시 8192 크기로 임포트된 텍스처는 이제 LOD 1 크기가 4096으로 제한되지 않고 8192로 표시됩니다. 다음 예제 이미지에서는 크기가 최대 8192인 텍스처를 사용할 수 있도록 프로젝트의 BaseDeviceProfiles.ini 파일을 수정했습니다. 텍스처 Texture_8k_Test가 로드되면 텍스처의 임포트된 크기와 표시되는 크기가 모두 8192임을 볼 수 있습니다.
이미지를 클릭하면 최대 크기로 볼 수 있습니다.
압축된 텍스처 메모리 요구량
DXT는 픽셀들을 팔레트 색상 및 보간 색상을 가진 4x4 블록으로 묶는 것을 기반으로 하는 손실 압축을 사용합니다. 그 결과 8:1 DXT1 및 4:1 DXT5는 불변 압축 파일 크기가 됩니다. 비디오 메모리 및 텍스처 풀의 리소스는 특정 플랫폼이나 하드웨어에 대해 고정되어 있기 때문에 텍스처의 해상도와 리소스의 사용 사이에 균형이 이루어져야 합니다. 아래의 표는 완전한 밉(1x1에서 완전 네이티브 밉0까지)을 가진 여러 일반적인 해상도에서의 DXT1과 DXT5에 대한 메모리 요구량을 나타낸 것입니다. 메모리 요구량은 텍스처 해상도 비율의 상수에 가까운 배수이며, DXT5의 메모리 요구량은 DXT1의 두 배에 가깝습니다.
압축 비율에 대한 해상도는 상수이므로, 이 표에 기재되지 않은 텍스처의 해상도에 대한 메모리 요구량을 계산하려면 해상 비율을 곱하기만 하면 됩니다. 예를 들면, 1024x512 텍스처의 메모리 요구량은 1024x1024 텍스처 메모리 요구량의 절반이 됩니다.
이 표의 데이터는 Box-Filter 밉 생성과 DirectX 텍스처 압축을 사용하여 ATI의 Compressonator로 만든 텍스처로부터 컴파일 되었습니다.
해상도 | 1x1부터 총 밉의 수 | DXT1 | DXT5 |
---|---|---|---|
16x16 | 5밉 | 312 bytes | 496 bytes |
32x32 | 6밉 | 824 bytes | 1.48kb (1,520 bytes) |
64x64 | 7밉 | 2.80kb (2,872 bytes) | 5.48kb (5,616 bytes) |
128x128 | 8밉 | 10.8kb (11,064 bytes) | 21.4kb (22,000 bytes) |
256x256 | 9밉 | 42.8kb (43,832 bytes) | 85.4kb (87,536 bytes) |
512x512 | 10밉 | 170kb (174,904 bytes) | 341kb (349,680 bytes) |
1024x1024 | 11밉 | 682kb (699,192 bytes) | 1.33MB (1,398,256 bytes) |
2048x2048 | 12밉 | 2.66MB (2,796,344 bytes) | 5.33MB (5,592,560 bytes) |
4096x4096 | 13밉 | 10.6MB (11,184,952 bytes) | 21.3MB (22,369,776 bytes) |
8192x8192 | 14밉 | 42.6MB (44,739,384 bytes) | 85.3MB (89,478,640 bytes) |
엔진 Config TextureGroup 프로퍼티
특정 게임 TextureGroups에 지원되는 최소 및 최대 LOD(밉)는 엔진의 환경설정 파일에 정의되어 있습니다.
환경설정 세팅 파일의 소스 세트는 [언리얼 엔진 4 설치 위치]\Engine\Config\BaseDeviceProfiles.ini
파일의 [/Scripts/Engine.TextureLODSettings] 부분에 있습니다.
게임 개발을 위해 [your_game]\Config\DefaultDeviceProfiles.ini
파일에도 Engine\Config\
폴더에 있는 기본 프로퍼티의 미러 세트가 들어 있습니다. 이 세트가 보통 게임의 특정 설정에 따라 변경되는 복사본입니다.
언리얼 에디터와 게임 내에 대해 각각 독립적인 TextureGroup 항목 세트가 있다는 점에 유의하세요. 두 세트는 각각 환경설정 파일의 [SystemSettingsEditor]와 [SystemSettings]에 있습니다.
DefaultDeviceProfiles.ini
파일의 TextureLODGroup 세팅 항목은 아래와 비슷합니다. 오래된 QA 버전에는 각 세팅에 대한 MinMagFilter 및 MipFilter 프로퍼티가 포함되어 있지 않을 수 있습니다.
[/Script/Engine.TextureLODSettings]
; 이 섹션의 항목은 모든 플랫폼에 영향을 미친다는 점에 주의하세요!!!
@TextureLODGroups=Group
TextureLODGroups=(Group=TEXTUREGROUP_World,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WorldNormalMap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WorldSpecular,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Character,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_CharacterNormalMap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_CharacterSpecular,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Weapon,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WeaponNormalMap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WeaponSpecular,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Vehicle,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_VehicleNormalMap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_VehicleSpecular,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Cinematic,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Effects,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=linear,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_EffectsNotFiltered,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Skybox,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_UI,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Lightmap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Shadowmap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,NumStreamedMips=3,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_RenderTarget,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_MobileFlattened,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Terrain_Heightmap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Terrain_Weightmap,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Bokeh,MinLODSize=1,MaxLODSize=256,LODBias=0,MinMagFilter=linear,MipFilter=linear,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Pixels2D,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=point,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
PC AppCompat 버킷
AppCompat은 시작 시에 수집되는 객관적이고 실증적인 증거를 바탕으로 다양한 SystemSettings를 오버라이드하는데 사용됩니다. AppCompat이 활성화되면(PC 한정) 시스템은 기기 용량을 측정한 다음 Engine.ini의 값을 '버킷' 5개 중 하나의 프리셋 값으로 덮어씁니다. 이 사용의 예시는 Engine\Config\
폴더의 BaseCompat.ini를 참조하세요.
AppCompat은 오직 게임(에디터가 아니라)이 처음 실행될 때 단 한번만 점검되도록 되어 있습니다. 게임이 처음 실행되는지 알기 위해 AppCompat은 이전에 기기를 위해 계산한 점수가 들어 있는 [game]Engine.ini에 [AppCompat] 섹션이 존재하는지 확인합니다. 이미 한 번 적용된 AppCompat은 다시 변경되지 않으므로 매번 덮어쓰는 일 없이 사용자가 직접 변경할 수 있습니다.
에디터에 대해서는 AppCompat이 비활성화되어 있으므로 기기 사양은 개발 중 다양한 기기에서 보이는 에셋의 모습에 영향을 주지 않습니다. 이것이 SystemSettings와 SystemSettingsEditor가 분리되어 있는 이유입니다.
빈 DefaultCompat.ini를 게임에 적용하면 효과적으로 AppCompat을 비활성화할 수 있습니다. 이 경우 Engine.ini에 있는 [SystemSettings]에서 모든 버킷이 초기화됩니다. 이 경우 시스템은 AppCompat이 도입되기 전과 똑같이 동작합니다.
TEXTUREGROUP 프로퍼티
TextureGroup의 각 항목은 게임의 렌더링에 사용되는 특정 텍스처 세트에 대한 텍스처의 프로퍼티를 정의합니다. 텍스처를 공통 세트로 그룹화하면 다양한 게임 텍스처 리소스에 의한 텍스처 메모리 풀의 사용을 보다 잘 제어할 수 있습니다.
프로퍼티 | 설명 |
---|---|
MinLODSize | 픽셀 단위로 지정되는 2의 제곱수로, 범위는 1~8192입니다. 이 값은 반드시 MaxLODSize보다 작아야 합니다. |
MaxLODSize | 픽셀 단위로 지정되는 2의 제곱수로, 범위는 1~8192입니다. 이 값은 반드시 MinLODSize보다 커야 합니다. |
LODBias | 렌더용으로 업로드하기에 앞서 오프셋하기 위한 밉 레벨의 수를 결정하는 음수 또는 양수의 값입니다. MinLODSize와 MaxLODSize 사이로 제한됩니다. |
MinMagFilter | 텍스처가 GPU에 의해 축소 또는 확대되었을 때 텍스처 필터 타입을 정의합니다 아래의 도표를 참조하세요. |
MipFilter | 먼 거리나 평행에 가까운 각도로 텍스처를 볼 때 GPU가 두 개의 밉을 블렌드해야 하는지 여부를 지정합니다. 아래의 도표를 참조하세요. |
NumStreamedMips | 스트림 인/아웃에 허용된 밉 수입니다. 텍스처에 10개의 밉이 있고 NumStreamedMips가 2라면 가장 높은 두 개의 밉에 대해서만 스트림 인/아웃이 허용됩니다. 따라서 텍스처는 언제든지 메모리에 8~10개의 밉을 갖게 됩니다. NumStreamedMips를 0으로 설정하면 스트림되는 밉이 없으며 이 LOD그룹을 사용하는 텍스처는 항상 전체 로드된다는 뜻입니다. NumStreamedMips를 -1로 설정하면(다른 제약은 계속해서 적용되지만) 모든 밉의 스트림 인/아웃이 허용된다는 뜻입니다. NumStreamedMips는 옵션 세팅으로, 디폴트는 -1입니다. |
필터링
MinMagFilter | MipFilter | 필터 유형 |
---|---|---|
point | 점 | |
linear | 점 | 양선형 |
linear | 삼선형 | |
aniso | 점 | 이방성 점 |
aniso | 이방성 선형 |
TextureGroup, LODGroup, LODBias
config ini 파일에서 지정된 TextureGroup과 LODBias 설정은 텍스처 프로퍼티에서 지정된 LODGroup 및 LODBias 세팅과 함께 개별 텍스처에 사용된 최종 텍스처 밉 세트를 결정합니다.
아래는 [your_game]Engine.ini의 TextureGroup 항목의 예시입니다.
Group=TEXTUREGROUP_World,MinLODSize=1,MaxLODSize=4096,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage
TEXTUREGROUP_World LODGroup에 배정된 텍스처는 모두 렌더링에 사용되는 밉 범위를 결정하기 위해 이 세팅을 사용하게 됩니다. 텍스처 프로퍼티의 추가적인 LODBias 설정은 config ini 파일의 TextureGroup에서 지정된 LODBias에 첨가됩니다.
LODBias는 렌더링을 위해 어느 밉이 선택되는지를 바이어스 또는 오프셋시킵니다. LODBias는 LODGroup의 최소/최대 범위 이전에 계산됩니다. 텍스처 프로퍼티에 있는 LODBias는 TextureGroup에 있는 LODBias에 첨가되어 사용되는 최종 LODBias 값을 결정합니다. LODBias 값 0은 메인(네이티브) 텍스처 해상도입니다. LODBias 값 1은 텍스처의 아래 첫 번째 밉, LODBias 값 2는 아래 두 번째 밉입니다. 예를 들어 1024x1024 텍스처의 LODBias가 1이면 렌더링을 위해 512x512 밉이 선택됩니다.
각 텍스처에 대해 텍스처 프로퍼티에서 지정된 LODBias 값은 양수일 수도 있고 음수일 수도 있습니다. 따라서 이는 TextureGroup의 기본 LODBias를 더 높거나 낮은 밉 값으로 오프셋할 수 있습니다. 예시는 다음과 같습니다.
- TextureGroup의 LODBias가 0이고 텍스처 프로퍼티의 LODBias가 0이면 최종 LODBias는 0이 됩니다.
- TextureGroup의 LODBias가 0이고 텍스처 프로퍼티의 LODBias가 1이면 최종 LODBias는 1이 됩니다.
- TextureGroup의 LODBias가 1이고 텍스처 프로퍼티의 LODBias가 1이면 최종 LODBias는 2가 됩니다.
- TextureGroup의 LODBias가 1이고 텍스처 프로퍼티의 LODBias가 -1이면 최종 LODBias는 0이 됩니다.
최종 LODBias가 계산된 다음에는 이 값이 TextureGroup의 최소/최대 LODSize 범위에 맞는지 확인하기 위해 텍스처 밉을 점검합니다. 필요한 경우에는 값이 조정됩니다. 이 과정을 통해 config ini 파일을 간단히 변경하여 특정 TextureGroup을 최소/최대 LOD 범위로 제한할 수 있습니다.
예를 들어 LODBias가 1인 1024x1024 텍스처가 위에서 보는 것처럼 TEXTUREGROUP_World LODGroup에 있는 경우에는 512x512 밉을 사용합니다. 그 다음, 이것이 TextureGroup의 최소 및 최대 LODSize 범위(이 경우에는 256~1024)에 부합하는지 확인하기 위한 점검이 이루어집니다. 각각의 게임에는 고유의 TextureGroup 설정이 있기 때문에 아티스트와 레벨 디자이너는 각 그룹의 MinLODSize와 MaxLODSize를 알아야 합니다. 2048 텍스처가 MaxLODSize 1024의 TextureGroup에 배정된 상태로 게임이 출시되면 렌더링 퀄리티에는 아무런 도움이 되지 않으면서 배포할 수 있는 패키지 사이즈만 늘어나게 될 것입니다.
텍스처 프로퍼티
다양한 텍스처 프로퍼티에 대한 설명은 텍스처 프로퍼티 페이지를 참조하세요.