본문으로 바로가기
반응형


유니티에서 이제 gamma가 아닌 Linear color space 사용이 일반적이 되었는데 이에 따라 프로젝트에서 gamma로 작업한걸 linear로 변경했을때 alpha 값이 의도한것과 다르게 나오는 문제가 생기고 있다.  급한대로 테스트 한거라 정확한 테스트 결과는 아니니 프로젝트 사용에 참고만 해주기 바란다.(틀린부분이 있다면 욕하지 말고 조용히 언급해주시길...)


우선 아래 이미지를 보고 Unity에서 알파채널 값이 텍스쳐 포맷에 따라 어떻게 출력되고 있는지를 확인해보자.


포토샵에서 하나의 이미지를 만들고 알파마스크를 생성해 50% grayscale로 알파를 빼줬다.

사용된 알파 채널(grayscale 50)

이를 PNG로 작성하고 똑같이 알파채널에 50%로 적용하면 아래와 같이 나온다.


2020.1버젼. URP Gamma color space. background color는 RGB 128, 128, 128. TGA의 경우 알파채널에 생성한 마스크 채널에 grayscale 50%를 적용해 알파를 생성한 이미지.(Legacy도 똑같이 나온다)


위 이미지와 같이 알파가 50%가 적용된 상태면 배경과 색이 같게 나오는 왼쪽처럼 나오는게 맞다.(즉 실제 PNG로 작업해서 알파를 생성한 이미지가 값으로 체크해보면 실제 값이 아닌 gamma correction을 적용 받아 출력되고 있는 상태이다)

이를 리니어로 바꾸면 아래와 같이 출력된다.

즉 TGA는 감마건 리니어건 알파채널값은 동일하게 리니어에서 계산하므로 문제가 없지만 PNG의 경우 엔진에서 Interaced된 영역을 기준으로 알파채널을 엔진에서 생성하기 때문에 실제 생성되는 알파값은 color space에 영향을 받는걸 확인 할 수 있다.


포토샵에서 color setting에서 Blend RGB Color using Gamma 옵션을 활용하는것도 하나의 방법이 될 수 있으나

이 경우에는 포토샵 레이어에서 blending option 을 사용한 경우 이미지의 느낌 자체가 틀어지기 때문에 추천하지 않는다.


다른 방식으로는 알클코더님이 제안해주신 Render texture를 활용해 sRGB를 해제하고 UI 캔버스를 통채로 렌더링 한 다음 거기다 gamma correction을 계산해 사용하는 방법이다.


https://www.slideshare.net/agebreak/color-space-gamma-correction


물론 이 경우는 Render texture 사용에 따른 메모리 사용량이 늘어난다....


세번째 방법은  vector mask를 사용해서 알파를 뺄 경우에는 level을 사용해서 alpha channel값을 decompressed 해주는 방법도 있다.


0.218을 포토샵 레벨에서 중간값 기준으로 곱해준다.


이를 PNG파일로 에디터에 올려보면


약간의 차이가 있지만 거의 의도한 대로 나온다. 물론 이 경우는 작업 영역을 벡터 마스크로 해야 한다는 전제가 붙는다.


마지막으로 고려해볼 만한 방법은... UI shader 기본 셰이더를 뜯어고치던가... 아니면 material 을 하나 만들어서 적용 하는 방법이다.



Shader "illu/UIGammaCorrection"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)

        _StencilComp("Stencil Comparison", Float) = 8
        _Stencil("Stencil ID", Float) = 0
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255

        _ColorMask("Color Mask", Float) = 15

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType" = "Plane"
                "CanUseSpriteAtlas" = "True"
            }

            Stencil
            {
                Ref[_Stencil]
                Comp[_StencilComp]
                Pass[_StencilOp]
                ReadMask[_StencilReadMask]
                WriteMask[_StencilWriteMask]
            }

            Cull Off
            Lighting Off
            ZWrite Off
            ZTest[unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask[_ColorMask]

            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0

                #include "UnityCG.cginc"
                #include "UnityUI.cginc"

                #pragma multi_compile_local _ UNITY_UI_CLIP_RECT
                #pragma multi_compile_local _ UNITY_UI_ALPHACLIP

                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                sampler2D _MainTex;
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float4 _MainTex_ST;

                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                    OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                    OUT.color = v.color * _Color;
                    return OUT;
                }

                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect) ;
                    #endif

                    #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001);
                    #endif
                  
                    color.a *= pow(color.a, 4.5);

                    return color;
                }
            ENDCG
            }
        }
}


이렇게 작성한 셰이더를 material에 넣어서 적용하면 된다.


수식의 유도는 linear를 UnityCG.cginc에 정의된 감마공간으로 변환하는데 사용하는 방정식을 참고했다.

inline half3 LinearToGammaSpace (half3 linRGB)
{
return max(1.055 * pow(linRGB, 0.416666667) - 0.055, 0);
}

대충 4.5정도 때려넣으니 비슷하게 나와서 저렇게 수식 작성한거니깐 제대로 할꺼면 여러가지 테스트 해서 수식 수정하는걸 권장함.


texture option의 Ignore PNG file gamma이 옵션이 이 역할을 해줄거라 생각했는데 아직은 아무 동작도 하지 않는다...


제일좋은건 UI아티스트가 TGA 포맷을 사용해서 알파작업을 해서 UI를 넘기면 모든 문제가 쉽게 해결되지만 이건 UI작업 환경에 대한 이해가 없는 문제라 그리 권장하지는 않는다. 다만, 위 작업 방식역시 담당 아티스트가 에디터에서 최종 아웃풋을 확인하면서 결과를 확인해야 하는 문제라 그리 간단치 않다는거...

texture inspector에서 저 ignore PNG file gamma 항목이 PNG의 알파채널 생성 알고리즘이 Linear로 계산된다면 모든것이 편안해진다.... 아니면 UI canvas는 gamma와 linear 렌더링을 선택할 수 있어야 하는데...이건 유니티 구조상 쉽지 않고..


좀 더 확실한 방법을 알게되면 다시 업데이트 할 예정...



부연설명 1.

결국은 gamma에서 알파값을 linear에서도 똑같이 나오게 조정해주면 되는 케이스가 은근 많다. 본문에서는 이 셋팅에 대한 부분은 굳이 다루지 않았지만 위에 소개된 방법들을 응용하면 두개의 셋팅을 일치시키는건 가능할 것으로 본다.


부연설명 2.

앞서 기록했던 PNG에서 background layer를 유지한 상태에서 벡터 마스크는 색이 비슷해서 헷갈린건데 알파가 안빠진다.. 요건 패스..

반응형

'Technical Report > Graphics Tech Reports' 카테고리의 다른 글

shader gamma test  (0) 2021.03.03
Uncharted 4 Technical art review  (0) 2020.12.07
DLSS 2.0 Compared screenshot  (0) 2020.05.16
Bent Normal  (0) 2020.03.13
Bent Normals and Cones in Screen-space  (0) 2019.04.12