유니티 엔진 (Unity Engine)

유니티 그래픽스 최적화 - 8.텍스처 (Texture)

원생계 2019. 9. 27. 20:26

.

.

8. 텍스처 (Texture)

8-1 인트로

텍스처는 주로 픽셀쉐이더 단계에서 사용. 텍스처 값 샘플링 > 최종컬러 반영. (Vertex Texture Fetch 기법은 논외)

참고 : 텍스처 사용은 아래 참고

https://youtu.be/-6iquaC0Hf4

8-2 대역폭 (Bandwidth)

텍스처 병목 = 메모리 대역폭에 종속( Memory Bandwidth )

이미지 데이터 사이즈가 커지면 대역폭 문제 발생. 프로세서가 한 번에 가져올 수 있는 데이터 크기 한정.

모바일 기기 더욱 이슈. 전력소모, 물리적으로 작아서 쿨링 미미, 발열 문제. 그래서 대역폭이 작게 설계.

CPU, GPU 뿐 아니라 메모리도 발열과 전력 소모에 큰 영향.

쓰로틀링(Throttling, Thermal Throttling) 기능, 발열 심해지면 성능 억제, 발열 감소.

GPU 가 메모리를 읽을 때, 텍스처를 읽을 때 병목. 텍스처 해상도가 중요.1024 쓰다가 2048 쓰면 4배 커짐.

텍스처 대역폭 문제 확인>

Edit > Project Settings > Quality > Rendering 텍스처 해상도 설정. Texture Quality : Full Res/Halg/Quarter

가장 쉬운 접근은 텍스처 해상도 줄이기. 모든 텍스처가 아니라, 하나의 텍스처 때문에도 병목 발생 가능. 모두 256 텍스처인데 한 장이 4096 이거나.

8-3 텍스처 압축(Texture Compression)의 필요성

디스크 용량과 메모리 크기 모두 절약. 일반적인 PNG, JPEG 압축과는 다른 특징.

텍스처는 고정된 비율 ‘블럭 형태의 손실 압축 기법’을 사용.

01) Bit Per Pixel

한 픽셀당 3개 채널 8bit * 3 = 24bit. 16,777,216 가지 컬러 = 트루컬러

30비트 모니터도 보급화 되고 있지만, 아직 대부분 24비트, 대부분 프로그램도 24비트.

알파채널이 있는 이미지는 RGBA = 32bit

32bpp 이미지 2048 이면 134,217,728비트(약 16메가) 알파채널 없는 24bpp 면 약 12메가.

02) 메모리

대역폭 문제도 있지만, 메모리 용량 문제도 있음. 물리 기반 렌더링은 오브젝트 하나에 사용되는 텍스처 양이 많음. 복잡한 씬이라면 메모리에 올라가는 텍스처가 많아 메모리 부족.

안드, iOS 등 모바일디바이스는 비디로/시스템 메모리가 하나의 메모리 같이 씀. 메모리 더 부족.

32bpp 2048 텍스처 20장 쓰면 320메가바이트. 디스크 용량도 문제. 너무 커지면 스토어에서 받을 때도 문제. 100MB 넘으면 WiFi 로만 받을 수 있어서 많은 게임들이 인게임 패치 시스템으로 동적 다운로드.

03) bpp와 색의 범위

이미지 정밀도, 즉 bpp를 줄이는 것도 간단 솔루션. 채널당 8비트 -> 4비트 사이즈 절반.

채널당 8비트 = 256 단계 밝기, 컬러 개수 = 16,777,216 개

채널당 4비트 = 16 단계 밝기, 컬러 개수 = 4096 개

색상 그라데이션의 마하밴드가 생기는 등 시각적 두드러짐이 커짐. 성능과 퀄리티 트레이드 오프.

04) 이미지 파일 용량 압축 포멧

크게 손실압축, 비손실압축 구분. png 는 비손실, jpeg는 손실. But, 게임에서 쓰기위에 VRAM 에 올리면 압축을 풀고 올려야 함. (decompress 비용 때문에) 즉, 2048 RGBA 이미지 원본 파일 2~3MB 인데, VRAM 에 올리면 16MB 로 올려짐. 그래야 GUI 가 텍스처로 처리하는데 포커싱.

그래서 GPU 가 실시간으로 텍스처를 읽어들일 수 있도록 텍스처를 위한 별도의 압축 방식이 존재. 실시간 렌더링에서의 특수한 텍스처 압축 포멧.

유니티는 어떤 이미지를 Import 하건 특정 이미지로 변환.

Settings > Android > Format : RGB Compressed ETC 4 bits 같은.

플렛폼별로 특성이 다름, 적절한 압축 포맷을 설정해주어야 함.

Override for Android, Override for iOS 플래그 체크해서 플랫폼별 세부 설정 활용.

유니티는 jpeg, png, psd, tiff 등 어떤 걸 사용해도 import할 때 특정 포멧으로 변환함. 편하게 관리하기 위해 psd 를 써도 무방함. 포토샵에서 매번 png 등으로 export 해서 관리할 필요 없음.

8-4 텍스처 압축의 조건

게임 텍스처 압축 조건

1. 인코딩이 되어 있는 채로 메모리에 존재해야 함

2. 랜덤 액세스 가능해야 함

3. 디코딩 속도가 빨라야 함

01) 하드웨어 지원

텍스처 압축 = 시스템 특화

압축된 텍스처 데이터에서 바로 샘플링 되어야 함. 즉 메모리에 로드될 때 압축이 풀리지 않고 압축된 데이터 그대로 적재되어야 함. = 압축 해제가 고속이어야 함. 디코딩이 하드웨어에 구현, 압축 포멧은 어떤 하드웨어냐에 따라 지원 여부 의존.

02) 랜덤 액세스

텍스처 = 이미지이지만 그래픽스 렌더링에서의 특징과 역할 때문에 별개 이름.

폴리곤에 매핑되어 보이는 것이 주목적. UV, ST 텍셀 좌표, 그려지는 픽셀이 어느 지점의 컬러를 가져와야 하느냐의 정보 좌표.

좌표를 가지고 값을 읽을 때 필터링도 수행. 필터링 point 면 GPU가 텍셀 하나만 읽으면 됨, 필터링 Linear 면 여러 개의 텍셀을 한 번에 읽어야 함. 고속으로 수행해야 함. 즉, 텍스처 샘플링이 성능에 영향을 주면 안 됨. 밉맵 단계별로 다양한 크기의 이미지도 텍스처 안에 있기 때문에 GPU가 랜덤 액세스, 특정 위치의 텍셀을 바로 읽어와야 함.

PNG같은 가변 비율 순차 방식 인코딩 데이터를 실시간 디코딩, 랜덤 액세스에 부적합. 특정 위치를 읽으려면 전체 데이터를 처음부터 읽어야 하기 때문에 성능 보장 못함.

03) 가변 비율 인코딩

쉬운 설명을 위해 문자열 압축 예시

AAAAAAAAABBBBBBBCCCC” (길이 20)

압축 > “9A7B4C” (길이 6)

=20:6

AAABBBCCCDDDEEEFFFGG”

압축 > “3A3B3C3D3E3F2G”

= 20:14

위 예시는 압축의 기본/간단 예시일 뿐, jpeg,png는 더 복잡. 핵심, 동일한 해상도 데이터라도 이미지 복잡도 등에 따라 압축률이 가변적이라는 것. 복잡한 이미지는 비교적 간단한 만화같은 이미지보다 압축 효율이 떨어짐.

350x350 이미지라도 복잡한 이미지는 205KB, 비교적 단순한 이미지는 115KB

아래 데이터에서 15번째 E문자를 알아내려면 처음부터 읽어야 알 수 있는데,

AAABBBCCCDDDEEEFFFGG”

이런 고정 압축 비율이 아닌 가변 압축 방식은 텍스처 압축에 부적합.

04) 고정 비율 인코딩

텍스처는 고정비율 압축률. 원본 이미지가 복잡하든 단순하든, 원본과 압축 데이터 크기 비율이 4:1, 8:1 등으로 고정. 특정 좌표 컬러를 읽을 때 처음부터 디코딩 하지 않아도 랜덤 액세스 가능.

주로 일정 크기의 블럭 단위로 텍스처 인코딩.

05) 손실 압축

인코딩 과정/시간은 상관 X, 디코딩은 쉽고 빨라야함. 시각적 퀄리티 너무 떨구지 않고 압축 효율은 높이고 디코딩은 빠른 알고리즘 = 원본 유지 못하는 손실 압축 알고리즘 채용.

8-5 텍스처 압축의 기법들

텍스처 압축 방식, 다양하고 하드웨어 의존적. 여러개 압축 기법이 존재함.

모바일 환경, 대표적인 압축기법 =PVRTC, ETC, ASTC

01) PVRTC

PowerVR Texture Compression = 2004년 발표, 텍스처 손실 압축 포맷

PowerVR 칩에서 지원, 아이폰, 아이패드 등 iOS 기기 주력

PowerVR 칩 쓰는 안드로이드 기기도 지원, 수는 많지 않음. 안드는 ETC가 주력. 유니티는 Target Platform 이 iOS 이면 PVRTC 을 기본으로 사용.

2bpp, 4bpp 정밀도 타입 나뉨. PVRTC1과 PVRTC2 버전이 존재, 알고리즘은 동일하나 품질 업그레이드. 아이폰/아이패드가 PVRTC2 를 지원하지 않아서 유니티도 PVRTC1 만 지원.

Non알파/알파 모두 지원, 4bpp, 2bpp 모드 모두 지원. 알파 포함 원본 32bpp, 압축률 각 8:1, 16:1.

알파채널이 없는 텍스처는 알파 정보가 없는만큼 RGB 정밀도가 할당됨.

데이터 구성 = 두 개의 저해상도 이미지 + 원본 해상도의 보정 정보 데이터

원본 이미지를 4x4 크기 블럭으로 나눔 (2bpp 모드면 8x4 크기)

블럭마다 64비트 데이터 사용.

원본이 32bpp 라면 4x4 에 512 비트가 필요하지만, 이를 64비트 데이터로 압축. 대신 인접합 데이터들을 저해상도로 묶고 복원하는 과정에서 압축률대비 높은 퀄리티를 수반.

< 이미지 출처 : PVRTC & Texture Compression User Guide, Imagination Technologies Limited >

https://www.imgtec.com/blog/pvrtc-the-most-efficient-texture-compression-standard-for-the-mobile-graphics-world/

이미지를 4x4 크기 블럭으로 나눔.

블럭의 대표색상 두 가지 A, B 선택 (적당히 섞을 수도)

두 개의 대표 색상은 각각 15bit, 16bit 로 저장

변조(보정)데이터 32bit(텍셀당 2bit 저장)

-> 각 색상 A, B 중 어느 걸 사용하는지 가중치 정보를 담음. 두 색을 보간 할수도.

변조 데이터의 Modulation Bits 는 2bit (?) 라서 보간 가중치 선택 사항 4가지. 보정하기 위해 Mode 1bit 사용.

4x4 크기 블럭 당 색A 15bit + 색B 16bit + 변조 32bit + 모드 1bit = 64bit 이미지 저장 가능.

32bpp 를 4bpp 로 저장 가능.

블럭 단위로 인코딩/디코딩 해서 블럭 단위 표시될 수 있다. 변조 데이터로 복원할 때 인접한 블럭의 대표 컬러간에 선형보간, 블럭화 완화.

대표 컬러 선택과 변조 데이터 본원 알고리즘은 PC용 DXT와 컨셉이 비슷하지만, DXT는 대표컬러 선형보간이 없음.

만화 스프라이트나 아이콘엔 좋지 않음. 기본적으로선형보간이 되기 때문에 블럭화는 완화되지만, 대신 번짐 현상 발생. 레트로풍의 픽셀아트 리소스는 애초에 해상도가 작게 만들어지므로 압축 포기하는게 나을수 있다.

02) ETC

참고하기 좋은 사이트 : https://hrmrzizon.github.io/2017/03/12/unity-texture-compression-2-erricsson-texture-compression/

ETC ( Ericsson Texture Compression ) 은 Ericsson 에서 PACKMAN 압축포맷 개량 2005년 발표. 크로노스 그룹에서 OpenGL ES 표준으로 지정. 안드로이드 거의 모든 해상도 ETC 지원.ETC1, ETC2 버전 존재. 알파채널 지원 추가 ETC2

사람 눈의 색차(chrominance)보다 휘도(luminance)에 더 민감하다는 사실.인코딩 시, 기본 색상, 정보와 휘도 정보로 나누어 저장, 디코딩에서 복원. 휘도 정보는 텍셀별로 저장, 색상 정보는 여러 텍셀을 묶어서 저장.

4x4 텍셀의 블럭마다 64bit 데이터로 저장.4x4 나는 텍셀 블럭을 다시 4x2 or 2x4 크기 서브 블럭으로 나눔. 가로세로 정보 1bit. 서브 블럭마다 대표 색상 결정. RGB444 12bit 로 저장.

<이미지 출처 : ETC2: Texture compression using Invalid Combinations, Ericsson Research >

http://www.jacobstrom.com/publications/StromWennerstenHPG2011.pdf

미리 하드웨어에 정의된 8개 휘도 테이블 중 하나 선택, 3bit에 저장.

휘도 테이블 : ( -8, -2, 2, 8 ) 에서 ( -127 -42 42 127 ) 까지 설정

서브블럭 내 텍셀들간 밝기 차이가 크지 않다면 0번 테이블 선택, 차이가 크면 7번 테이블 선택하는 식.

테이블 선택, 기본 색상에 4개 밝기를 가감해서 4가지 색 생성가능.

1:1 해상도로 텍셀마다 서브 블럭에서 휘도 테이블의 인덱스 값이 2bit 로 저장,

텍셀 별 최종 색상 추출.

각 서브블럭당 32bit ( flip 1bit + 대표색 12bit + 휘도테이블 인덱스 3bit + 텍셀 휘도 16bit ) x 2 = 64bit

원래의 24bit 이미지라면 4x4 텍셀에 384bit 필요, 384 : 64 = 6:1 압축률. 24bpp -> 4 bpp 압축.

기본 색상과 밝기 차이로 데이터를 나누기 때문에 색상 차이가 큰 이미지는 화질 저하 두드러짐. ETC2 은 ETC1 의 단점 보완. 서브블럭당 대표 색상 2개 선택, 블럭당 총 4개 대표컬러. 색상 차이가 커도 품질 저하 보완.

ETC1 은 알파채널 처리 없, ETC2 는 알파채널 정보 포함 가능.(bpp 증가) ETC2 는 OpenGL ES 3.0 이상에서만 지원. 갤S3 이하 구형 기기는 OpenGL ES 2.0 라서 ETC2 불가.

ETC2 지원 안 되는 기기도 정상 구동은 되는데, 이 경우 비압축 RGBA32 기본값으로 압축이 풀려서 메모리 저장. 성능상 좋지 않음. ETC2 미지원 기기에서 텍스처별로 압축포멧 설정 가능.

텍스처는 ETC2 압축인데, 지원되지 않을 경우 Override ETC2 fallback 을 32bit (half resolution) 으로 하면 해상도 절반 절약.

보급형 디바이스 타겟인 가벼운 게임이면 ETC1을, 고퀄리티 고성능 디바이스 요구 게임이면 ETC2 를.

SystemInfo.graphicsDeviceVersion 으로 ES 버전 확인 가능.

QualitySettings.SetQualityLevel 로 퀄리티 변경 가능.

또, ETC1 은 알파채널 지원 안 됨. 그렇다고 Open GL ES 2.0 에서 RGBA 16/32 만 사용하라는 건 아님. 텍스처 포멧을 RGB Compressed ETC 4bit 로 선택, 하단 Compress using ETC1(split alpha channel) 플래그. 원본 RGBA 가 유니티 내부적으로 두 개의 ETC1로 나눠서 하나는 RGB, 하나는 알파채널 정보를 다뤄, ETC2 사용 안하고도 알파채널 사용 가능. 단, 이 기능은 스프아리트 및 UI 만 작동. 일단 쉐이더에선 알파채널 없는 텍스처로 처리. 또, Alpha split 처리는 2개 텍스처 슬롯이므로 쉐이더도 두 개의 텍스처 읽는 처리 필요. 텍스처 병목이라면 부담 2배.

fixed4 SampleSpriteTexture ( float2 uv ) {

fixed4 color = tex2D (_MainTex, uv);

#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED

if (_AlphaSplitEnabled)

color.a = tex2D (_AlphaTex, uv).r;

#endif // UNITY_TEXTURE_ALPHASPLIT_ALLOWED

return color;

}

03) ASTC

ASTC ( Adaptive Scalable Texture Compression ) ARM에서 개발하고 2012년 크로노스 그룹에서 OpenGL ES 표준으로 지정.

용량과 품질 트레이드 오프 가능. ETC, PVRTC는 bpp가 한정. ASTC는 8bpp ~ 0.86bpp 품질 조절 가능. 가변 블록 크기를 사용하기 때문. ETC, PVRTC 는 압축되는 블럭 크기가 4x4 로 고정. ASTC 는 압축되는 기준 블럭 크기를 4x4, 12x12 블럭으로 다양하게 선택 가능.

블럭당 128bit 사용. 128/16=8bpp 가 되고

12x12 블럭이면 128/144 = 약0.89bpp 가 되는 것.

유니티에서도 텍스처 압축 포멧 다양하게 선택 가능

RGB Compressed ASTC 4x4 block

RGBA Compressed ASTC 5x5 block

RGB Compressed ASTC 6x6 block

RGB Compressed ASTC 8x8 block

RGB Compressed ASTC 10x10 block

RGBA Compressed ASTC 12x12 block

등등

텍스처 중요도/복잡도 따라 블럭 크기 개별적 선택 가능. 캐릭터는 4x4, 파티클같이 뭉개져도 괜찮으면 12x12 로 용량 절약도 가능.

ASTC는 ETC보다 복잡한 인코딩. ETC 보단 일반적 품질 좋음. ASTC 는 안드, iOS 모두 지원. 문제는 지원 기기의 범위. Open GL ES 3.2 이상 or Open GL ES 3.1+AEP(Android Extension Pack) 지원 기기 사용가능.

삼성 갤S6, LG G5 이상 디바이스들. iOS는 Metal 지원하는 A8 processor 사용 시작 기종부터, iPhone6, iPad mini 4 이상 기기.

구형 기기도 지원하려면 ASTC 사용 어렵.

8-6 POT

블럭 기반인 이유로 해상도 제약. ETC, PVRTC는 2의 n승 해상도 필. POT 텍스처라고 부름. Power of Two. NPOT는 반대. NPOT 텍스처를 ETC, PVRTC 로 설정하면 경고 메시지. 아틀라싱으로 자연스럽게 해결.

8-7 16비트 디더링(16-bit dithering)

손실 압축으로 화질 열화, 32bpp 보단 16bpp, 즉 RGBA16 으로 줄이고 디더링(Dithering)처리. 디더링으로 마하밴드를 없앨 수 있다.

https://github.com/keijiro/unity-dither4444

유니티에 내장 X 일본 유니티 지사에서 만든 유니티용 16비트 이미지 디더링 플러그인, 유니티 활용 가능.

위 링크 참조.

Editor 폴더 TextureModifier.cs 스크립트. Dither.png 파일 import, 16비트 디더링 이미지로 import 됨.

Texture Type 이 Editor GUI and Legacy GUI 갈제설정된 부분은 코드를 고쳐야 함.

TextureModifier.cs

void OnPreprocessTexture()

{

...

importer.textureType = TextureImporterType.GUI; // 이 라인을 제거.

8-8 크로마 서브샘플링 (Chroma Subsampling)

UI, 2D 스프라이트 또 다른 손실압축 방식

유니티 ChromaPack 플러그인. 원본대비 품질 저하 없고 1bit 알파채널 지원. 그라데이션 알파블랜딩 무리.

원본 32bit이미지를 픽셀 당 12bit 사용하여 표현. ETC, PVRCT 에 비해 압축 효율은 떨어지지만 화질 저하가 거의 없음. 쉐이더로 구현되어 있기 때문에 디코딩은 GPU 에서. CPU 부하는 없다.

https://github.com/keijiro/ChromaPack

오래된 플러그인이라 수정 필요.

CameraAdjuster.cs (14)

camera.orthographicSize = (float)screenHeight / 512;

=>

GetComponent<Camera>().orthographicSize = (float)screenHeight / 512;

cp로 끝나는 파일명을 가진 이미지는 ChromaPackProcessor.cs 스크리비트로 크로마 서브샘플링용 이미지로 인코딩.

채도(chroma)와 휘도(luma)로 분리하여 샘플링하는 기법. 원본 이미지를 Cb, Cr, Y 세 정보로 나누어 변환.

변환된 쉐이더는 디코딩 처리가 이루어져, 전용 쉐이더로 렌더링 되어야 함.

불투명 이미지는 ChromaPack/Opaque 쉐이더

알파채널 이미지는 ChromaPack/Cutout 쉐이더

1bit 알파채널, 추가 쉐이더 부담 제약으로 범용성은 무리. 카드게임 일러같이 화질 거하에 민감한 게임에 부분적 사용 고려.

8-9 밉맵 (Mipmap)

LOD개념. 내부적으로 여러 크기 단계의 텍스처 생성. 런타임 렌더링 시 픽셀 쉐이더에서 텍스처를 읽을 때 상황에 맞는 크기 픽. 성능은 절약, 메모리는 33% 증가. 유니티 기본은 밉맵 활성화. 텍스처 인스펙터에서 Generate Mip Map 해제.

항상 카메라 가까이 있거나, 작아질 일이 드문 오브젝트면 밉맵 꺼도 됨. 사이드뷰 횡스크롤 게임도.

밉맵 사용하지 않는 텍스처는 Quality Settings / Texture Quality 적용 받지 않아 다운사이징 되지 않고 원본 해상도로 메모리에 올라감.

8-10 Max Size

플렛폼별 텍스처 해상도 크기 강제 제한. Max Size 가 1024 이면, 이보다 작은 텍스처는 원본, 큰 텍스처는 1024로 리사이징 됨.

8-11 Read / Write Enabled

텍스처 Import 셋팅 Read/Write Enabled 플래그도 메모리 영향. 활성화 되면 텍스처 데이터가 CPU 메모리에 상주. 유저 스크립트에서 Texture.GetPixel, Texture.SetPixel 등의 코드를 통해 텍스처의 읽기/쓰기 가능여부. 런타임, 로딩타임에 텍스처 읽고쓸 수 있어짐.

단, 거의 그럴일 없으므로 메모리를 위해 비활성화.

.

유니티 그래픽스 최적화 스타트업 : coupa.ng/bOkEbG

 

유니티 에반젤리스트의 유니티 그래픽스 최적화 스타트업

COUPANG

www.coupang.com

 

 

MSI 지포스 GTX 1660 SUPER 벤투스 S OC 그래픽카드 D6 6GB

COUPANG

www.coupang.com

.

728x90
반응형