[Unity] URP 셰이더 BRDF(양방향 반사도 분포 함수)

2023. 6. 13. 14:00Unity

728x90
반응형

BRDF는 Bidirectional Reflectance Distribution Function의 약자이다. 어떻게 반사할지를 정의하는 함수이다.

요즘의 사실적인 BRDF는 PBS(Physically Based Shading)라고 부르는 물리 기반 셰이딩 기법을 사용한다. 유니티는 Smoothness, Metallic, BaseColor 값으로 사실적으로 보이도록 한다.

(*참고 디즈니 BRDF 자료 링크)

 

 

Lambert BRDF

Shader "Custom/BRDF/Lambert"
{
    Properties
    {
        [MainTexture] _BaseMap("Base Map", 2D) = "white" {}
        [MainColor][HDR]_BaseColor("BaseColor", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        Pass
        {
            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/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
                float3 normalOS     : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float2 uv           : TEXCOORD0;
                float3 normal       : TEXCOORD1;
                float3 lightDir     : TEXCOORD2;
            };

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                half4 _BaseColor;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                OUT.normal = TransformObjectToWorldNormal(IN.normalOS);
                OUT.lightDir = normalize(_MainLightPosition.xyz);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // 이걸 안 하면 버텍스 사이 픽셀 노멀의 길이가 1이 아닌 것들이 발생함.
                IN.normal = normalize(IN.normal);

                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                color *= _BaseColor;
                float NdotL = saturate(dot(IN.normal, IN.lightDir));
                half3 ambient = SampleSH(IN.normal);
                half3 lighting = NdotL * _MainLightColor.rgb + ambient;
                color.rgb *= lighting;

                return color;
            }
            ENDHLSL
        }
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}

 

 

Half Lambert BRDF

밸브 게임사에서 만들었던 BRDF이다. 그냥 램버트가 특정 경계에서 극단적으로 어두워지는 느낌이라면 하프 램버트는 부드럽게 라이팅 된다.

Shader "Custom/BRDF/HalfLambert No Ambient"
{
    Properties
    {
        ...
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        Pass
        {
            ...

            half4 frag(Varyings IN) : SV_Target
            {
                // 이걸 안 하면 버텍스 사이 픽셀 노멀의 길이가 1이 아닌 것들이 발생함.
                IN.normal = normalize(IN.normal);

                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                color *= _BaseColor;
                float NdotL = saturate(dot(IN.normal, IN.lightDir) * 0.5 + 0.5);
                //half3 ambient = SampleSH(IN.normal);
                //half3 lighting = NdotL * _MainLightColor.rgb + ambient;
                half3 lighting = NdotL * _MainLightColor.rgb;
                color.rgb *= lighting;

                return color;
            }

            ENDHLSL
        }
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}

Ambient를 주석으로 껐고 saturate 함수를 적용하기 전에 " * 0.5 + 0.5 " 계산으로 하프 램버트를 적용했다는 것을 볼 수 있다. 원래 -1이었던 값은 0.5를 곱하고 0.5를 더하여 0이 되고, 원래 0이었던 값은 0.5를 곱하고 0.5를 더하여 0.5가 되어 조금 완만한 곡선을 그리는 것을 위의 첨부한 사진에서 볼 수 있다.

과거에 엔진 성능이 부족해서 쓰던 방식이라 지금은 쓰이는 일이 없고 GI 환경광과 같이 사용하면 오히려 너무 밝아지게 된다.

 

 

Lambert NoAmbient

Shader "Custom/BRDF/Lambert No Ambient"
{
    Properties
    {
        ...
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        Pass
        {
            ...
            
            half4 frag(Varyings IN) : SV_Target
            {
                // 이걸 안 하면 버텍스 사이 픽셀 노멀의 길이가 1이 아닌 것들이 발생함.
                IN.normal = normalize(IN.normal);

                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                color *= _BaseColor;
                float NdotL = saturate(dot(IN.normal, IN.lightDir));
                //half3 ambient = SampleSH(IN.normal);
                //half3 lighting = NdotL * _MainLightColor.rgb + ambient;
                half3 lighting = NdotL * _MainLightColor.rgb;
                color.rgb *= lighting;

                return color;
            }

            ENDHLSL
        }
        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}

다른 부분들은 Lambert BRDF와 같고 프래그먼트 쪽이 약간 달라졌다. half3 두 줄을 주석으로 처리하여 Ambient를 끈 이유는 환경 라이트의 개입 없이 램버트 라이팅만 확인하기 위함이다.

 

 


참고자료

 

아티스트를 위한 유니티 URP 셰이더 입문 - YES24

셰이더(Shader)를 HLSL(High Level Shader Language)로 다루고 싶은 아티스트를 위한 입문서다. 아티스트들의 눈높이에 맞추어 기초 그래픽스 이론과 셰이더 개념을 설명하고, 이를 유니티 엔진에서 활용할

www.yes24.com

728x90
반응형