본문 바로가기
SHADER

알파 텍스쳐 메모리 절약을 위한 방법 - 알파 텍스쳐 ETC1 2장으로 처리하기

by tartist 2016. 7. 25.

유니티에서 제공하는 안드로이드용 알파 채널 텍스쳐중 디바이스 호환성이 뛰어난 것은 3가지 밖에 없습니다.

RGBA32 BIT / RGBA 16BIT / ETC2 8BIT

 

ETC 2 8Bit는 아직 디바이스 점유율이 안좋아 사용이 불가한 상황입니다.. 현재 시장에 배포된 디바이스중 50프로는 사용 불가..

(점유율 현황 - https://developer.android.com/about/dashboards/index.html --- Open GL Version 이 2.0인 것들은 지원하지 않습니다.)

 

 

결론적으로 RGBA 16 Bit 와 RGBA 32 Bit 두 종류밖에 쓸 수 없다는 건데..

RGBA 32 Bit 는 무압축 포맷이라 메모리 이슈가 빵빵터져 앱이 계속 죽어 나갈 것이고..(특히 아이폰.. 6도 1기가라는..)

RGBA 16 Bit 는 쓰레기 품질에 압축률도 엉망이라 아트를 볼 줄 아는 사람이 한명이라도 있는 프로젝트에서는 거품 물고 반대할 것이 뻔합니다.

 

1024의 알파 채널이 있는 텍스쳐를 사용하게 될 경우

RGBA 32 Bit의 용량은 4MB

RGBA 16 Bit의 용량은 2MB

 

사이즈가 작은 캐주얼 게임이라면 별 상관없을거 같긴한데.. 대규모  RPG같은 알파 텍스쳐 사용량이 100개 이상 넘어가는 프로젝트라면

너무 큰 클라이언트 용량으로 출시가 힘든 상황이 올수도 있습니다. 특히 중국쪽은 더더욱..

 

 

이런 문제들을 해결하기 위해서 Etc1 4Bit 2장을 이용하면 간단히 해결이 됩니다. 한장은 오파큐텍스쳐로 쓰고 한장은 알파텍스쳐로 쓰면 되지요..

 

1024 텍스쳐의 Etc1 4bit의 용량은 고작 512 KB~~ 2장을 써도 1024 KB~~  쓰레기 품질의 RGBA 16Bit 한장을 쓸때보다도 1/2 용량이라는 어마어마한 이득을 취하면서도 괜찮은 품질을 얻을 수 있습니다.

 

이 방법을 쓰기 위해서는 셰이더를 새로 짜고 텍스쳐 분리 작업을 하고 여러가지 해야할 일거리가 여기저기 터지지만 메모리 이슈로 게임을 출시 못하는 거보다는 낫지 않을까?

 

이방법을 쓸 때 유의 사항.

1. 1드로우였던게 2드로우가 되므로 드로우콜에 유의하자.

2. 알파 채널이 필요한 경우도 반드시 생기므로 알파 채널에 대한 연산 부분도 포함하자.

 

 

아래는 etc1 2장으로 처리할 수 있는 간단한 알파 블렌드 셰이더 코드 입니다.

 

 

Properties    {
        _TintColor ("Tint Color"Color) = (0.5,0.5,0.5,0.5)
        _MainTex ("MainTex"2D) = "white" {}
        _Alpha ("Alpha"2D) = "white" {}
    }

    SubShader {

        Tags {"IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent"}
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

        uniform sampler2D _MainTex; 
        uniform fixed4 _MainTex_ST;

        uniform sampler2D _Alpha; 
        uniform fixed4 _Alpha_ST;

        uniform fixed4 _TintColor;

        struct v2f 
        {
            fixed4 pos : SV_POSITION;
            fixed2 uv : TEXCOORD0;
            fixed2 uv1 : TEXCOORD1;
            fixed4 color : COLOR;
        };


        v2f vert (appdata_full v) 
        {
            v2f o;

            o.color = v.color;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);

            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            o.uv1 = TRANSFORM_TEX(v.texcoord, _Alpha);

            return o;
        }

        fixed4 frag(v2f i) : COLOR {
                 fixed4 _MainTex_var =  tex2D(_MainTex,i.uv);
                fixed4 _Alpha_var = tex2D(_Alpha,i.uv1);

                  fixed3 emissive = _MainTex_var.rgb * i.color.rgb;
                  fixed node_9936 = _Alpha_var.r;     

                fixed3 finalColor = emissive * (_TintColor * 2);

                return fixed4(finalColor, (i.color.a * _MainTex_var.a * node_9936));
            }
            ENDCG
        }
    }//SubShader