[Unity] URP 셰이더 HLSL Lambert 셰이더

2023. 6. 10. 14:49Unity

728x90
반응형
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"
    }
}

 

 

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

Lambert 라이팅을 위해 URP 셰이더 라이브러리 함수와 변수들이 추가로 사용되었기 때문에 라이브러리가 추가되었다.

 

 

            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;
            };

float3 normalOS는 오브젝트 공간의 버텍스 노멀 벡터이다. 아래에 Varyings vert(Attributes IN) 함수에서 Attributes IN 변수로 버텍스 셰이더에 전달되고 월드 공간의 벡터 좌표로 변경된다.

float3 normal과 float3 lightDir은 아래에 Varyings OUT 변수에서 사용된다. OUT은 half4 frag(Varyings IN) : SV_Target 함수에 Varyings IN으로 전달되어서 프래그먼트 셰이더의 입력 매개변수로 사용된다. 이 경우에는 라이팅을 프래그먼트 셰이더에서 계산하고 버텍스 셰이더는 라이팅의 방향 정보만 계산했다.

 

 

            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;
            }

해당 프래그먼트 셰이더에서는 버텍스 셰이더로부터 전달받은 노멀 정보를 IN.normal = normalize(IN.normal); 줄로 정규화를 한다. Lambert 라이팅 할 때 코사인 dot 연산 시 정확하지 않게 보간 되기 때문에 정규화를 진행해야 한다. 정규화를 진행하면 벡터의 방향을 유지한 채 길이만 1로 변경된다.

            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;
            }

바로 위의 함수에서도, OUT.lightDir에서 lightDir는 빛의 방향 벡터인데 _MainLightPosition 변수의 값을 정규화해서 사용한다.

 

 

                float NdotL = saturate(dot(IN.normal, IN.lightDir));
                half3 ambient = SampleSH(IN.normal);
                half3 lighting = NdotL * _MainLightColor.rgb + ambient;
                color.rgb *= lighting;

Lambert 라이팅을 실제 계산하는 부분이다. Lambert 라이팅에서 중요한 요소는 빛의 방향 벡터와 면의 노멀 벡터인데 IN.normal과 IN.lightDir를 dot 함수로 계산한 뒤 saturate하는 것을 볼 수 있다.

 

 

        UsePass "Universal Render Pipeline/Lit/ShadowCaster"

마지막 라인은 HLSL이 아닌 ShaderLab의 명령어이고 그림자를 생성하는 역할이다.

 

 

 


참고자료

 

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

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

www.yes24.com

728x90
반응형