본문으로 바로가기

Fragment Lambert Lit

category Technical Report/Unity Shader 2019. 2. 9. 14:50
반응형




Lambert Lighting을 fragment에 적용해서 비교해 본다.(surface 하면서 간단한건 슬슬 tutorial에 넣어야 할듯해서...)


원문은 여기 참조 : https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html

기본 구조에 대한 설명은 : https://illu.tistory.com/1197

우선 Surface code



   Shader "Test/SurfaceLambert" {
    
   Properties
    {
      _MainTex("Texture", 2D) = "white" {}
    }

    SubShader {
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM

    #pragma surface surf SimpleLambert noambient

    half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten)
    {

    half NdotL = max(0, dot(s.Normal, lightDir));
    half diff = NdotL ;
    half4 c;

    c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten);
    c.a = s.Alpha;


    return c;
     }

    struct Input
    {
    float2 uv_MainTex;
    };

    sampler2D _MainTex;

    void surf(Input IN, inout SurfaceOutput o)
    {
    o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
    }
    ENDCG
    }
    //   Fallback "Diffuse"

}



일단 Surface shader에서는 ambient color와 shadow를 그리고 있으므로 fallback과 ambient color를 안받도록 했다.



원래라면 이렇게 나오는게 맞다.



샘플 라이팅 코드에서 그냥 복붙해서 공부한다면 이렇게 나올수도 있다... 알아서 고치던가 그냥 넘어가자... [각주:1]


아래는 Fragment code




  Shader "Test/FragmentLambert"
 
   {
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            // indicate that our pass is the "base" pass in forward rendering pipeline. It gets ambient and main directional
            // light direction in _WorldSpaceLightPos0 and color in _LightColor0
            Tags {"LightMode"="ForwardBase"}
        
            CGPROGRAM
               #pragma vertex vert
               #pragma fragment frag
               # include "UnityCG.cginc"
               # include "UnityLightingCommon.cginc" // for _LightColor0

            struct v2f
                {
                float2 uv : TEXCOORD0;
                fixed4 diff : COLOR0; // diffuse lighting color
                float4 vertex : SV_POSITION;
                };

            v2f vert(appdata_base v)
                {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;

 /*
    struct appdata_base
    {
       float4 vertex : POSITION;
       float3 normal : NORMAL;
       float4 texcoord : TEXCOORD0;
      UNITY_VERTEX_INPUT_INSTANCE_ID
    };

  */
                // get vertex normal in world space
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);

                // dot product between normal and light direction for standard diffuse (Lambert) lighting
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                // factor in the light color
                 o.diff = nl * _LightColor0;

                 return o;
                }

                sampler2D _MainTex;

                fixed4 frag(v2f i) : SV_Target
                {
               
                fixed4 col = tex2D(_MainTex, i.uv);
               
                // multiply by lighting
                col *= i.diff;
               
                return col;
                 }

            ENDCG
        }
    }
}




서피스 코드와 비교해보면 lightDir이 _WorldSpaceLightPos0.xyz로 바뀐것과 s.Normal을 worldNormal로 구해서 계산하는 것 정도의 차이다. (Light color는 둘다 _LightColor0 사용)


object to World space를 구하지 않고 v.normal로 노멀값을 바로 사용할 경우에는 오브젝트를 돌리면 이렇게 음영이 따라 돌아간다. 뭐 이런게 필요할때는 이렇게 써야지~



Built in shader 변수에서는 아래와 같이 정리하고 있다.
https://docs.unity3d.com/kr/current/Manual/SL-UnityShaderVariables.html


Light 파라미터는 셰이더에서 어떤 렌더링 경로가 사용되는지, 어떤 LightMode 패스 태그가 사용되는지에 따라 다른 방법으로 셰이더에 전달됩니다.

포워드 렌더링(ForwardBase 및 ForwardAdd 패스 타입):
        

이름

 Type

내용

  _LightColor0 (Lighting.cginc)

 fixed4

 Light Color

  _WorldSpaceLightPos0

float4

 Directional lights: (world space direction, 0). Other lights: (world space position, 1)

  _LightMatrix0 (AutoLight.cginc)

float4x4

 world-to-light matrix. sample cookie나 감쇠 텍스쳐에 사용

   unity_4LightPosX0

   unity_4LightPosY0

   unity_4LightPosZ0

float4

  (ForwardBase pass only) world space positions of first four non-important point lights.

   unity_4LightAtten0

float4

  (ForwardBase pass only) attenuation factors of first four non-important point lights.

   unity_LightColor

half4[4]

  (ForwardBase pass only) colors of of first four non-important point lights.

   unity_WorldToShadow

float4x4[4]

  World-to-shadow matrices. One matrix for spot lights, up to four for directional light cascades. 그림자 매트릭스. 스팟라이트 하나와 디렉셔널 라이트 캐스케이드를 최대 4개 지원한다.


디퍼드 렌더링에서는 아래와 같이 지원한다.(UnityDeferredLibrary.cginc에 선언되어 있다)

 이름

  Type

 내용

 _LightColor

 float4

 Light color.

 _LightMatrix0

  float4x4

 World-to-light matrix. Used to sample cookie & attenuation textures.

 unity_WorldToShadow

float4x4[4]

 World-to-shadow matrices. One matrix for spot lights, up to four for directional light cascades.






자 이제 여기에 ambient color를 더해보자.



   o.diff.rgb += ShadeSH9(half4(worldNormal, 1)); 


유니티는 ambient color 계산에 구면 조화(spherical harmonics)함수를 사용한다.

이에대한 자세한 설명은 고팀장님 블로그 포스팅으로 >> https://blog.naver.com/tigerjk0409/221447554201



// normal should be normalized, w=1.0
half3 SHEvalLinearL0L1 (half4 normal)
{
    half3 x;

    // Linear (L1) + constant (L0) polynomial terms
    x.r = dot(unity_SHAr,normal);
    x.g = dot(unity_SHAg,normal);
    x.b = dot(unity_SHAb,normal);

    return x;
}

// normal should be normalized, w=1.0
half3 SHEvalLinearL2 (half4 normal)
{
    half3 x1, x2;
    // 4 of the quadratic (L2) polynomials
    half4 vB = normal.xyzz * normal.yzzx;
    x1.r = dot(unity_SHBr,vB);
    x1.g = dot(unity_SHBg,vB);
    x1.b = dot(unity_SHBb,vB);

    // Final (5th) quadratic (L2) polynomial
    half vC = normal.x*normal.x - normal.y*normal.y;
    x2 = unity_SHC.rgb * vC;

    return x1 + x2;
}


half3 ShadeSH9 (half4 normal)
{
    // Linear + constant polynomial terms
    half3 res = SHEvalLinearL0L1 (normal);

    // Quadratic polynomials
    res += SHEvalLinearL2 (normal);

#   ifdef UNITY_COLORSPACE_GAMMA
        res = LinearToGammaSpace (res);
#   endif

    return res;
}





이제 Light Probe도 적용 받는다.


자 이제 그림자 패스를 추가해주자.


  // shadow caster rendering pass, implemented manually
   // using macros from UnityCG.cginc
        Pass
        {
            Tags {"LightMode"="ShadowCaster"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            struct v2f { 
                V2F_SHADOW_CASTER;
            };

            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG


이제 잘 나온다.



하지만 self shadowing이 구현되지 않았다. 코드에 shadow를 추가해준다.


아래는 전체 코드이다.


Shader "Test/FragmentLambert"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
CGPROGRAM

#pragma vertex vert
#pragma fragment frag
# include "UnityCG.cginc"
# include "Lighting.cginc"
// # include "UnityLightingCommon.cginc"
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
# include "AutoLight.cginc"

struct v2f
{
float2 uv : TEXCOORD0;
SHADOW_COORDS(1) // put shadows data into TEXCOORD1
fixed3 diff : COLOR0;
fixed3 ambient : COLOR1;
float4 pos : SV_POSITION;
};

v2f vert(appdata_base v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
half3 worldNormal = UnityObjectToWorldNormal(v.normal);
half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
o.diff = nl * _LightColor0.rgb;
// o.diff.rgb += ShadeSH9(half4(worldNormal, 1));
o.ambient = ShadeSH9(half4(worldNormal, 1));

// compute shadows data
TRANSFER_SHADOW(o)
return o;
}

sampler2D _MainTex;

fixed4 frag(v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
fixed shadow = SHADOW_ATTENUATION(i);
// darken light's illumination with shadow, keep ambient intact
fixed3 lighting = i.diff * shadow + i.ambient;
col.rgb *= lighting;

return col;

}
ENDCG
}
// shadow casting support
// UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
// Shadow pass를 추가하지 않고 이렇게 UsePass도 대체할수 있다.

Pass
{

Tags { "LightMode" = "ShadowCaster"}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
# include "UnityCG.cginc"

struct v2f
{
V2F_SHADOW_CASTER;
};

v2f vert(appdata_base v)
{

v2f o;

TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)

return o;
}

float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}

ENDCG
}

}
}



최종 아웃풋.

Shader variants는 6 / 33 개.(surface는 더 쥐어짤 여지가 있긴 하다)














  1. 이건 왜 이런고 하니 Unity example lighting sample코드에서 max(0, n)값으로 0아래 값을 절삭하지 않아서 이렇게 나온다...(오래된건데 안바꾸나... 깨알같은 회사디스..ㅋ) [본문으로]
  2. VertexLit pass도 있지만 그냥 생략 [본문으로]
반응형