본문 바로가기
Shader

Lit 셰이더 분석

by 대마왕J 2023. 9. 28.
#define kDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)

으로 정의되어 있으므로 

half OneMinusReflectivityMetallic(half metallic)
{
    // We'll need oneMinusReflectivity, so
    //   1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
    // store (1-dielectricSpec) in kDielectricSpec.a, then
    //   1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
    //                  = alpha - metallic * alpha
    half oneMinusDielectricSpec = kDielectricSpec.a;
    return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}

이 되어 있어서 Reflectivity 는 유전체일때 0.04 도체일때 1이 나오도록 되어 있다. 

half3 brdfDiffuse = albedo * oneMinusReflectivity;
half3 brdfSpecular = lerp(kDieletricSpec.rgb, albedo, metallic);

이므로 

유전체일때 
brdfDiffuse 는 albedo * (1-0.04) 가 되고 
brdfSpecular 는 kDieletricSpec.rgb 즉 float3(0.04, 0.04, 0.04) 가 된다. 
가 된다. 뭐 유전체의 스페큘러는 흑백이고 유전체의 반사율은 4% 이므로 맞는듯. 
전도체일때에
brdfDiffuse 는 albedo * 0 이 되고 
brdfSpecular 는 albedo 가 된다.

 

그래서 실시간 라이트 연산을 보자면
half3 brdf = brdfData.diffuse;
로 스페큘러를 뺀 디퓨즈 반사율에 알베도를 곱한 것이 들어가고 그게 brdf에 들어간다
아니 brdf라고 이름붙이면 좀 곤란하지 않나? 이미 Albedo가 곱해져 버렸잖아. 
흠... 아니 칼라 brdf라고 생각하면 될지도 

어쨌건 디퓨즈 연산이 이렇게 되었으므로 
스페큘러 연산이 여기에 더해져야 하는데, 
brdf += brdfData.specular * DirectBRDFSpecular(brdfData, normalWS, lightDirectionWS, viewDirectionWS);
공식이 이거임. 

그럼 brdfData.specular 가 뭔지 봐야하는데, 위에서 설명했듯
비금속일때는 0.04 흑백이고 금속일때는 albedo임. 

이걸 일단 구현해보면 

Shader "Lecture7"
{
    Properties
    {
        _BaseColor ("Main Color", color) = (1, 1, 1, 1)
        _BaseMap ("Main Texture", 2D) = "white" { }
        _Metallic ("Metallic", range(0, 1)) = 0
    }
    
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        cull off

        Pass
        {
            Tags { "LightMode" = "UniversalForward" }

            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Frag

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

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseColor;
                float4 _BaseMap_ST;
                float _Metallic;
            CBUFFER_END

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);
            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float4 texcoord : TEXCOORD;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float3 normalWS : NORMAL;
                float4 uv : TEXCOORD;
                float4 vertexSH : TEXCOORD1;
                float3 viewWS : TEXCOORD2;
            };

            Varyings Vert(Attributes input)
            {
                Varyings output = (Varyings)0;

                //Transform
                float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
                float3 positionVS = TransformWorldToView(positionWS);
                float4 positionCS = TransformWorldToHClip(positionWS);
                // float4 positionCS = TransformObjectToHClip(input.positionOS.xyz);

                output.positionCS = positionCS;
                output.vertexSH.rgb = SampleSHVertex(output.normalWS);
                output.uv.xy = input.texcoord;
                output.viewWS = normalize(GetWorldSpaceViewDir(positionWS));
                output.normalWS = TransformObjectToWorldDir(input.normalOS);
                return output;
            }

            #define kDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04)

            half4 Frag(Varyings input) : SV_Target
            {
                
                //MainLight
                Light light = GetMainLight();
                float3 lightDir = light.direction;
                float3 lightColor = light.color;
                
                //Variables
                float3 normalWS = input.normalWS;

                //texture
                float4 albedo = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(input.uv, _BaseMap));
                
                half NdotL = saturate(dot(input.normalWS, lightDir));
                half3 radiance = lightColor * NdotL;

                //brdf
                half oneMinusDielectricSpec = kDielectricSpec.a;
                half oneMinusReflectivity = oneMinusDielectricSpec - _Metallic * oneMinusDielectricSpec;
                half3 brdfDiffuse = albedo * oneMinusReflectivity;
                half3 brdfSpecular = lerp(kDieletricSpec.rgb, albedo, _Metallic);

                // return float4(brdfDiffuse + brdfSpecular, 1);


                float3 brdf = (brdfDiffuse);
                brdf = brdf * radiance;
                
                return float4(brdf, 1);
            }
            ENDHLSL
        }
    }
}

메탈일때 디퓨즈는 0가 되고 비메탈일때 디퓨즈는 Albedo * (1-0.4) 가 됨 

                float3 brdf = (brdfSpecular);
                brdf = brdf * radiance;
                
                return float4(brdf, 1);

스페큘러를 보면 

메탈릭이 0일때 0.4가 되고, 메탈릭이 1일때 albedo가 됨. 

반응형

'Shader ' 카테고리의 다른 글

Lit 셰이더 분석 2  (0) 2023.10.10
매크로 상수 배열 정의하기  (0) 2023.02.15
카메라 디렉션 구하기  (0) 2022.10.04
OpenGL Transform 개요  (0) 2022.06.04
노말맵 블렌딩  (0) 2022.03.06

댓글