[Function] unity runtime compress texture| 유니티 런타임 환경에서 텍스처 압축하기

sonsazang

·

2022. 5. 19. 01:29

본 글은 회사 업무 중에 서버에서 불러오는 Texture2D (JPG, PNG)등의 용량이 너무 커서 램 관리가 필요한 모바일 환경에서 강제 종료되는 현상을 방지하기 위해 구현하게 된 기능입니다. 

 

필요한 기능

서버에서 불러오는 이미지를 런타임 환경에서 불러오는 과정과 동시에 텍스처 압축을 진행하여서 메모리 과부하를 막는 기능이 필요합니다.

유니티 프로젝트 안에 있는 텍스처의 경우 따로 여러 플랫폼에 원하는 형식으로 저장할 수 있는 기능이 제공되고 있으나 서버에서 바로 불러올 경우 위 기능을 사용하지 못하는 형식입니다. 

유니티 프로젝트 안에 설치된 이미지의 인스펙터 창 내부에 있는 플랫폼별 압축 설정 기능

 

구현한 기능

PNG or JPG -> GPU 랜더링이 필요하지 않는 이미지 파일들을 더 낮은 용량인 RGBA32로 변환 후 DXT5로 압축 진행

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Experimental.Rendering;

public class textureCompress : MonoBehaviour
{
    // Start is called before the first frame update

    public RawImage oldRaw; // 테스트 확인을 위한 RawImage 
    public RawImage newRaw;

    public Texture2D oldTex; // 테스트 확인을 위해 셋팅하는 이미지
    public Texture2D newTex;

    void Start()
    {
        oldRaw.texture = oldTex;
        newTex = ResizeTexture(oldTex);
        newRaw.texture = newTex;
    }
	
    // 이미지 리사이징 함수
    public Texture2D ResizeTexture(Texture2D oldTexture)
    {
        Texture2D copy = DuplicateTexture(oldTexture); // 읽기 가능하도록 설정
        Color[] oldColor = copy.GetPixels(); // 읽기 가능한 파일의 픽셀값 저장
        copy.Resize(oldTexture.width, oldTexture.height, TextureFormat.RGBA32, false);
        // RGBA32로 변환 (사이즈 유지)
        copy.SetPixels(oldColor); // 이전에 저장해둔 픽셀값 적용 (안할경우 이미지 단색)
        copy.Compress(true); // 추가로 압축 진행 (DXT5로 적용됨 확인)
        copy.Apply(); // 저장

        return copy; 
    }

    // Texture2D를 RenderTexture에 넣은 다음 이미지를 새로 읽어서 사용
    // 이 방식으로 진행해야 오류 없이 이미지 읽기(readable) 가능
    public Texture2D DuplicateTexture(Texture source)
    {
        RenderTexture renderTex = RenderTexture.GetTemporary(
                    source.width,
                    source.height,
                    0,
                    RenderTextureFormat.Default,
                    RenderTextureReadWrite.Linear);

        Graphics.Blit(source, renderTex);
        RenderTexture previous = RenderTexture.active;
        RenderTexture.active = renderTex;
        Texture2D readableTexture = new Texture2D(source.width, source.height);
        readableTexture.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
        readableTexture.Apply();

        RenderTexture.active = previous;
        RenderTexture.ReleaseTemporary(renderTex);

        return readableTexture;
    }
}

위 코드를 빈 게임오브젝트 생성 후 적용 한 다음에 확인을 위한 Canvas와 RawImage 2개를 설치합니다.

왼쪽은 압축전 텍스처, 오른쪽은 압축 후 텍스처가 적용될 로우이미지 2개 생성

높은 해상도를 가진 이미지 적용 후 실행

왼쪽은 압축 전 오른쪽은 압축 후 차이가 있는가?
압축 진행 전 이미지 - 32.0MB
압축 진행 후 이미지 - 8.0MB

보여지는 이미지는 압축 전이나 후를 구분하기 어려울 수준이지만 차지하는 용량의 차이를 볼수 있다. 

32.0MB 에서 압축 후 -> 8.0MB로 줄었다. 
픽셀 수는 동일하게 2048 * 4096으로 해상도는 동일한 것을 확인할 수 있다.

위 방식으로 불러오는 이미지에 적용하면 로직이 완성되는 부분이다.

 

이미지 한 두장에서는 차이가 적을수도 있으나 서버에서 이미지를 불러오는 경우엔 보통 많은 이미지를 불러오는 경우로 생각합니다. 위 로직을 잘 사용하셔서 조금 더 최적화된 이미지를 사용했으면 좋겠습니다. 

 

출처

테스트 이미지 -> eberhard grossgasteiger 님의 사진, 출처: Pexels

로직 -> https://stackoverflow.com/questions/44733841/how-to-make-texture2d-readable-via-script