11 – 01 ~ 02 커스텀 라이트 기본형 / Lambert 라이트 연산 (Unity Shader)

11 – 01 ~ 02 커스텀 라이트 기본형 / Lambert 라이트 연산 (Unity Shader)

텍스처 한 장만 받는 기본형 쉐이더를 만듭니다.

직접 확인해보기 위해서 아래 에셋을 사용했습니다. (무료)

https://assetstore.unity.com/packages/3d/characters/humanoids/barbarian-warrior-75519 <- TestAsset

Shader "Custom/CustomLight"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf Lambert noambient  
        // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다.
        #pragma target 3.0
        
        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {

            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

Lambert 라이트를 삭제하고 자신만의 라이트 이름을 지어서 커스텀 라이트를 구동시켜 보겠습니다.

바뀌거나 추가된 내용을 하나씩 보겠습니다.

Lambert 대신에 _MyLight로 바뀌었습니다.

여기는 이름을 마음대로 지어도 상관없습니다.

우리가 만든 커스텀 함수의 모습입니다.

이전까지는 void surf 사용했습니다.

이 함수는 float4로 시작합니다. 함수가 float4 를 반환 하므로 return float4  출력값을 가지고 있습니다. 

이어서 나오는 이름도 Lighting + “커스텀 라이트 이름” 을 추가해 주어야 합니다.

SurfaceOutput 구조체를 통째로 받아옵니다.

float3 lightDir 은 이전 파트에서 배웠던 조명 방향의 벡터 입니다.

단, 조명 계산을 편하게 사용하기 위해서 뒤집혀지고 길이가 1인 단위 벡터의 상태입니다.

float atten

atten ( attenuation ) 감쇠

그림자를 받거나 거리가 멀어지면서 점점 조명이 흐려지는 라이트의 거리별 감쇠 현상을 나타냅니다.

내가 빛을 받는 것을 없앨 때 사용하는 부분입니다.


Lambert 라이트 연산

우리가 만든 커스텀 라이트에서 변경합니다.

Shader "Custom/CustomLight"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf _MyLight noambient  
        // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다.
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {

            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }

        
        float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten)
        {
            float ndotl = dot(s.Normal, lightDir);
            // dot 노멀 백터와 라이트 벡터를 내적 연산
            // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음

            return ndotl;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

lightDir은 버텍스에서 바라보는 조명의 방향인 뒤집힌 방향 이기 때문에 두 벤터를 단순하게 내적해주면 cos 값이 나오게 됩니다.

위와 같은 코드는 가장 밝은 곳이 1 / 어두워 지는 경계선은 0 / 가장 어두운 곳은 -1 값으로 되어서

나중에 추가 라이트를 비추거나 할 때 문제가 됩니다. 

이 현상을 방지하기 위해서  saturate (값) 함수를 사용합니다.

max 함수도 사용하지만 최대 값이 없습니다.

max(0, dot(s.Normal, lightDir));

float ndotl = max(0, dot(s.Normal, lightDir));

Shader "Custom/CustomLight"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf _MyLight noambient  
        // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다.
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {

            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }

        
        float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten)
        {
            float ndotl = saturate(dot(s.Normal, lightDir));

            // dot 노멀 백터와 라이트 벡터를 내적 연산
            // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음
            // saturate 0 이하 값을 0으로 만들어줌
            
            // float ndotl = max(0, dot(s.Normal, lightDir));
            // max 함수도 있지만 최대 값이 없습니다.

            return ndotl;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

NormalMap 적용

Shader "Custom/CustomLight"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _BumpMap("Normal Map", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface surf _MyLight noambient  
        // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다.
        #pragma target 3.0

        sampler2D _MainTex;
        sampler2D _BumpMap;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };

        void surf (Input IN, inout SurfaceOutput o)
        {

            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            float3 d = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
            o.Normal = d;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }

        
        float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten)
        {
            float ndotl = saturate(dot(s.Normal, lightDir));

            // dot 노멀 백터와 라이트 벡터를 내적 연산
            // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음
            // saturate 0 이하 값을 0으로 만들어줌
            
            // float ndotl = max(0, dot(s.Normal, lightDir));
            // max 함수도 있지만 최대 값이 없습니다.

            return ndotl;
        }

        ENDCG
    }
    FallBack "Diffuse"
}

환경광 제거

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to Top