WinCNT

URP에서 깊이를 기록하는 Depth Map 셰이더 만들어보기! + After Effect의 이미지의 레벨 조정(Levels adjustment) 흉내기기! 본문

Unity/URP or Shader 관련

URP에서 깊이를 기록하는 Depth Map 셰이더 만들어보기! + After Effect의 이미지의 레벨 조정(Levels adjustment) 흉내기기!

WinCNT_SSS 2024. 1. 23. 10:48

서론

Depth Map은 (카메라) 시점으로부터 물체 표면과의 거리와 관련된 정보가 담긴 Map을 의미한다

한국어로는 깊이 맵(Wikipedia에는 왠지 깊이 지도로 되어 있지만…)이라고도 하고, 일본어로는 深度マップ(심도 맵)으로 부르기도 한다

 

Depth Map은 진짜 정말로 많은 곳에 쓰이는데 이번에는 애니메이터 분이 PV의 피사계심도(Depth Of Field)에서 쓰고 싶다는 요청이 있어서 만들어봤다

 

‘엥? DOF 그거 URP에 이미 있지 않음?’이라 생각할 수 있는데 필자도 그렇게 생각한다

하지만 애니메이터 분의 말을 들어보니, 유니티의 DOF는 키 프레임을 입력하는 방법을 몰라서 개인적으로 영상 제작 쓰기 힘들다고 하시더라

그리고 그냥 카메라 앞에 Quad를 두는 방식이 알기 쉽다고도 하심ㅎㅎ 그렇긴 하지


Depth Map Shader 자체는 어렵지 않다…

사실 Depth Map Shader 자체는 그다지 어렵지 않을 뿐더러 공식 문서에도 아예 예제가 나와있다

Reconstruct the world space positions of pixels from the depth texture | Universal RP | 14.0.10

 

Reconstruct the world space positions of pixels from the depth texture | Universal RP | 14.0.10

Reconstruct the world space positions of pixels from the depth texture The Unity shader in this example reconstructs the world space positions for pixels using a depth texture and screen space UV coordinates. The shader draws a checkerboard pattern on a me

docs.unity3d.com

 

해당 예제는 체크 무늬까지 있어서 좀 복잡하기만 그 부분을 빼면 Depth Map Shader이다

(복잡한 부분은 전무 체크 무늬 알고리즘이다)

 

다른 부분은 그래도 쓰면 되고 프래그먼트 셰이더는 딱 depth를 구하고 출력하는 부분까지만 작정하면 된다

half4 frag(Varyings IN) : SV_Target
{
    float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;

    #if UNITY_REVERSED_Z
        real depth = SampleSceneDepth(UV);
    #else
        real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
    #endif

    return half4(depth,depth,depth,1);
}

 

근데 환경 설정이나 셰이더의 상태에 따라서 위의 셰이더가 작동할 수도 있고 안 할 수도 있다

이제 그 부분에 대해서 알게된 점을 정리해보고자 한다


SSAO나 DepthOnly/DepthNormals Pass가 있으면 일단 된다

이부분에 대해서는 다음의 글에서 정리했으니 자세한 설명은 생략하도록 하겠다

https://wincnt-shim.tistory.com/393

 

Unity의 Depth Texture에는 실제로 어떤 오브젝트가 기록되는 걸까?(feat. SSAO)

서론 Depth Texture를 구현해보던 중에 조금 의문이 생겨서 조금 조사해봤다 참고로 구현하려던 건 아래의 예제이다 Reconstruct the world space positions of pixels from the depth texture | Universal RP | 14.0.8 Reconstruct

wincnt-shim.tistory.com

 

 


그럼 반드시 SSAO나 DepthOnly/DepthNormals Pass 등이 필요한가요?

결론부터 말하자면 No! 그렇지 않다

SSAO 처리를 위해서는 Depth가 필요하며, 또 그걸 위해서는DepthOnly/DepthNormals Pass가 필요할 뿐이다

불투명 오브젝트에 한해서는 이 방법 외에도 더 간단히 Depth Map을 만들 수 있는 방법을 찾았기에 정리해보고자 한다


Depth Map 출력에 필요한 설정들

Editor > Project Settings > Quality에 설정된 URP Asset의 Depth Texure를 On으로 한다

 

URP Asset의 데이터에서 Depth Texture Mode를 After Opaques로 설정한다

 

카메라 앞에 둘 Quad의 Render Queue는 반투명으로 한다

 

이러면 짜잔~!

SSAO나 DepthOnly/DepthNormals Pass가 없어도 Depth가 출력된다


한계점

이 방식으로는 반투명 오브젝트의 Depth를 얻을 수 없다

아래 이미지의 오브젝트는 왼쪽부터 커스텀 Unlit(Depth Pass 없음), URP의 디폴트 Lit(Opaque), URP의 디폴트 Lit(Transparent)인데 가장 오른쪽의 오브젝트들만 Depth가 출력되지 않는 것을 볼 수 있다


네? 레벨 기능(?)도 있음 좋겠다구요?

반투명이 안 찍힌다는 한계점이 있지만 어차피 이번엔 반투명 오브젝트가 대상도 아니라서 이걸로 끝!

 

…그렇게 생각하던 시기가 저에게도 있었습니다

이야기를 들어보니 Depth를 그대로 쓰는 것이 아니라 그걸 Levels adjustment해서 쓰고 있다는 것이었다!

 

근데 Levels adjustment이라니…그게 뭐지? RPG인가?

그래서 바로 물어보니 포토샵이나 After Effect에 있는 색상의 레벨 조정 기능이었다

 

어떻게 동작하는지만 알면 유니티의 셰이더에서도 같은 것을 할 수 있을 거 같아서 여러모로 움직여보거나 조사를 해봤다

「レベル補正」を使ったプロフェッショナルな色調補正 | Photoshop 色調補正ゼミナール | Shuffle by COMMERCIAL PHOTO

 

「レベル補正」を使ったプロフェッショナルな色調補正 | Photoshop 色調補正ゼミナール | Shuffle by

--> 「これさえ読めば、何も知らないところから、デジタル画像の色調を完全にコントロールできるようになれる!」を、合言葉に連載している「Photoshop 色調補正ゼミナール」。 今までの回

shuffle.genkosha.com


이미지의 레벨 조정(Levels adjustment)의 Input Level 추가

조사해보니 레벨 조정은 이미지의 채널의 색상 값(0~255)의 입력과 출력을 제한하거나 정규화하는 방식인 것 같았다

 

그래서 이리저리 실험해 봤는데 레벨 조정 기능의 Input Level은 얼추 비슷하게 구현할 수 있었다!

참고로 Input Level이란 바로 이 부분! 왼쪽부터 Black Input Level, Gamma, White Input Level이다

 

이 때 사용한 것이 Min-Max Normalization(최소-최대 정규화)이다

그리고 뭔가 특정 값 부분에서 급격히 값이 차이나길래 Reverse(?) Gamma Correction하니 거의 비슷하게 작동했다

마지막으로 Gamma 입력 값은 잘 몰랐지만 그냥 역수를 제곱(POW)하니 적당히 비슷하게 동작하는 것 같았다

Gamma란게 대부분 제곱하는 경우가 많은 것 같아서 해보니 됐다

 

출력 쪽도 추가하고 싶었지만 아쉽게도 더 급한 작업이 생겨 이 정도로 마무리하게 되었다

half4 frag(Varyings IN) : SV_Target
{
    float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;

    #if UNITY_REVERSED_Z
        real depth = SampleSceneDepth(UV);
    #else
        real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
    #endif

    const float blackInputLevel = _BlackInputLevel / 255;
    const float whiteInputLevel = _WhiteInputLevel / 255;
    const float gamma = _Gamma;
    const float blackOutputLevel = _BlackOutputLevel / 255;
    const float whiteOutputLevel = _WhiteOutputLevel / 255;
    
    // Reverse Gamma Correction
    depth = pow(depth, 1/2.2);

    // Min-Max Normalization
    depth = (depth - blackInputLevel) / (whiteInputLevel - blackInputLevel);
    depth = pow(depth, 1.0 / gamma);
    
    // Reverse Gamma Correction
    depth = pow(depth, 2.2);
    
    return half4(depth,depth,depth,1);
}

DepthMap의 샘플 코드

아래는 샘플 코드 전문이다

Shader "PV/DepthMap"
{
    Properties
    {
        [Header(Input Level)]
        [Space(5)]
        _BlackInputLevel("Black Input Level", Range(0.0, 253.0)) = 0.0
        _WhiteInputLevel("White Input Level", Range(2.0, 255.0)) = 255
        [PowerSlider(110)] _Gamma("Gamma", Range(9.9, 0.1)) = 1.0
    }

    SubShader
    {
        Tags { "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" "Queue" = "Transparent" }

        Pass
        {
            Cull Off
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

            CBUFFER_START(UnityPerMaterial)
            float _BlackInputLevel;
            float _WhiteInputLevel;
            float _Gamma;
            float _BlackOutputLevel;
            float _WhiteOutputLevel;
            CBUFFER_END
            
            struct Attributes
            {
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                float2 UV = IN.positionHCS.xy / _ScaledScreenParams.xy;

                #if UNITY_REVERSED_Z
                    real depth = SampleSceneDepth(UV);
                #else
                    real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
                #endif

                const float blackInputLevel = _BlackInputLevel / 255;
                const float whiteInputLevel = _WhiteInputLevel / 255;
                const float gamma = _Gamma;
                const float blackOutputLevel = _BlackOutputLevel / 255;
                const float whiteOutputLevel = _WhiteOutputLevel / 255;
                
                // Reverse Gamma Correction
                depth = pow(depth, 1/2.2);
                
                depth = (depth - blackInputLevel) / (whiteInputLevel - blackInputLevel);
                depth = pow(depth, 1.0 / gamma);
                
                // Reverse Gamma Correction
                depth = pow(depth, 2.2);
                
                return half4(depth,depth,depth,1);
            }
            ENDHLSL
        }
    }
}

결과

이건 이번 작업의 예시!


마무리

Depth Map를 위한 설정을 알게 된 것도 좋았지만, 레벨 조정을 독자적으로 잘 구현됐다는 게 특히나 만족스러운 작업이었다

출력 레벨? 그건 나중에 시간 나면 해보자😂


참고 사이트

Reconstruct the world space positions of pixels from the depth texture | Universal RP | 14.0.10

 

Reconstruct the world space positions of pixels from the depth texture | Universal RP | 14.0.10

Reconstruct the world space positions of pixels from the depth texture The Unity shader in this example reconstructs the world space positions for pixels using a depth texture and screen space UV coordinates. The shader draws a checkerboard pattern on a me

docs.unity3d.com

「レベル補正」を使ったプロフェッショナルな色調補正 | Photoshop 色調補正ゼミナール | Shuffle by COMMERCIAL PHOTO

 

「レベル補正」を使ったプロフェッショナルな色調補正 | Photoshop 色調補正ゼミナール | Shuffle by

--> 「これさえ読めば、何も知らないところから、デジタル画像の色調を完全にコントロールできるようになれる!」を、合言葉に連載している「Photoshop 色調補正ゼミナール」。 今までの回

shuffle.genkosha.com