Skip to content

Latest commit

 

History

History
130 lines (106 loc) · 5.69 KB

shadow_receiver.md

File metadata and controls

130 lines (106 loc) · 5.69 KB

Abstract

그림자를 표면에 나타나게 만들어보자

Shader

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
Shader "UnityShaderTutorial/shadow_receiver" {
    Properties {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {
        Pass {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            // compile shader into multiple variants, with and without shadows
            // (we don't care about any lightmaps yet, so skip these variants)
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            // shadow helper functions and macros
            #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.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"
    }
}

Description

위의 코드는 현재 물체가 광원에 의해 만들어지는 그림자를 표면에 표현하도록 만들어준다.

해당 코드가 정상적으로 동작하기 위해서는 shadow caster pass가 필수적으로 포함되어 있어야 한다. 해당 단계에서 생성되는 shadow map에 현재 물체가 포함되어야 하기 때문이다. 아래의 코드가 shadow_caster 에서 구현한 pass를 실행시켜주는 구문이다.

UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"

코드를 분석해보자. 위의 코드는 shadow caster에서 본 코드에 AutoLight.cginc 에 선언되어 있는 매크로 함수를 통해 표면의 색상을 변경시켜주는 일을 한다.

매크로 함수는 SHADOW_COORDS(1), TRANSFER_SHADOW(o), SHADOW_ATTENUATION(i) 총 3개가 사용된다.

SHADOW_COORDS(1)는 구조체 내부에 선언되며 해당 vertex에 필요한 버퍼의 정보를 가져오기 위해 shadow map의 uv좌표를 저장하는 역할을 한다.

TRANSFER_SHADOW(o)SHADOW_COORDS(1)로 선언된 변수에 uv좌표를 저장하는 역할을 한다. 이 함수는 해당 vertex의 월드 좌표를 이용해서 shadow map의 uv좌표를 가져온다. UNITY_NO_SCREENSPACE_SHADOWS가 선언되어 있지 않으면 uv좌표는 ComputeScreenPos() 함수의 리턴값이 된다.

// #if defined(UNITY_NO_SCREENSPACE_SHADOWS)
#define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );
// #else
#define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);

SHADOW_ATTENUATION(i)는 이전 pass에서 만들어놓은 shadow map에서 값을 가져와 카메라 시점의 깊이 값과 비교하여 그림자의 값을 결정하는 작업을 한다. 이 함수의 리턴값을 디퓨즈 값과 곱하여 검게 표현할지 원래의 색을 표현할지 결정한다. soft shadow 옵션을 사용하지 않으면 리턴값은 0 또는 1이 된다.

#define SHADOW_ATTENUATION(a) unitySampleShadow(a._ShadowCoord)

#if defined(UNITY_NO_SCREENSPACE_SHADOWS)
    UNITY_DECLARE_SHADOWMAP(_ShadowMapTexture);
    #define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_WorldToShadow[0], mul( unity_ObjectToWorld, v.vertex ) );
    inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord) {
        #if defined(SHADOWS_NATIVE)
            fixed shadow = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord.xyz);
            shadow = _LightShadowData.r + shadow * (1-_LightShadowData.r);
            return shadow;
        #else
            unityShadowCoord dist = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, shadowCoord.xy);
            // tegra is confused if we use _LightShadowData.x directly
            // with "ambiguous overloaded function reference max(mediump float, float)"
            unityShadowCoord lightShadowDataX = _LightShadowData.x;
            unityShadowCoord threshold = shadowCoord.z;
            return max(dist > threshold, lightShadowDataX);
        #endif
    }

#else // UNITY_NO_SCREENSPACE_SHADOWS
    UNITY_DECLARE_SCREENSPACE_SHADOWMAP(_ShadowMapTexture);
    #define TRANSFER_SHADOW(a) a._ShadowCoord = ComputeScreenPos(a.pos);
    inline fixed unitySampleShadow (unityShadowCoord4 shadowCoord) {
        fixed shadow = UNITY_SAMPLE_SCREEN_SHADOW(_ShadowMapTexture, shadowCoord);
        return shadow;
    }

#endif

Prerequisites