UnityShader Shadowの記述備忘録


# 影の書き込みに必要な記述を追う

Shadowのバッファーへの書き込みと、影を受ける側の処理を把握する為にビルトインシェーダーの記述を追ってみる。2019.3系のビルトインシェーダーを参考にします。
ひとまず記述が簡潔そうなVertexLitの影の書き込みを見てみる。

```Mobile-VertexLit.shader
    // Pass to render object as a shadow caster
    Pass
    {
        Name "ShadowCaster"
        Tags { "LightMode" = "ShadowCaster" }

        ZWrite On ZTest LEqual Cull Off

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

        struct v2f {
            V2F_SHADOW_CASTER;
            UNITY_VERTEX_OUTPUT_STEREO
        };

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

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

適当に整理して重要な部分だけ抜粋するとこう。

```
        Tags { "LightMode" = "ShadowCaster" }
        ZWrite On ZTest LEqual Cull Off

        #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)
        }

```

把握するべきは`V2F_SHADOW_CASTER;`と`TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)`と`SHADOW_CASTER_FRAGMENT(i)`っぽい。
一応考えやすい様に併記しておくと`appdata_base`はこう定義されている。

```UnityCG.cginc
struct appdata_base {
    float4 vertex : POSITION;
    float3 normal : NORMAL;
    float4 texcoord : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};
```

`UNITY_VERTEX_INPUT_INSTANCE_ID`はGPUInstancing対応の為の記述なので、頂点座標,法線,UV座標を持っているだけの構造体と思っておけば良い。

前述の３つの記述を詳しく追ってみる。

## V2F_SHADOW_CASTER;
### 結論
展開するとこうなる。

```
struct v2f {
    //V2F_SHADOW_CASTER;
    float4 pos : SV_POSITION
    float3 vec : TEXCOORD0; //これはグラフィックAPIとSHADOWS_CUBEキーワード次第
};
```

###マクロを読む
こんなマクロになっている。

```UnityCG.cginc
// Declare all data needed for shadow caster pass output (any shadow directions/depths/distances as needed),
// plus clip space position.
#define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS UNITY_POSITION(pos)
```

`V2F_SHADOW_CASTER_NOPOS`と`UNITY_POSITION(pos)`をそれぞれ確認してみる。
まず簡単な`UNITY_POSITION(pos)`。これはときどき見かける。

```HLSLSupport.cginc
// On D3D reading screen space coordinates from fragment shader requires SM3.0
#define UNITY_POSITION(pos) float4 pos : SV_POSITION
```
単純にクリップ座標を入れる変数を宣言しているだけ。
次に`V2F_SHADOW_CASTER_NOPOS`。

```UnityCG.cginc
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    // Rendering into point light (cubemap) shadows
    #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;
    //...省略...
#else
    //...省略...
#endif
```
コメントを読むとポイントライトのシャドウを表現する場合はこれが構造体に追加される**場合がある**ようですね。
`SHADOW_CUBE`はCubemapでシャドウマップを表現する時に有効になるkeywordのよう？cginc内で定義されていないようなので、実行時にCPU側からEnableされていそう。
`SHADOWS_CUBE_IN_DEPTH_TEX`は以下のように定義されているのでプラットフォームが"depth-format cube shadow map"に対応していない場合はに`float3 vec : TEXCOORD0;`が構造体に追加されるという事になりそう。

```UnityCG.cginc
#if defined(SHADER_API_D3D11) || defined(SHADER_API_PSSL) || defined(SHADER_API_METAL) || defined(SHADER_API_GLCORE) || defined(SHADER_API_GLES3) || defined(SHADER_API_VULKAN) || defined(SHADER_API_SWITCH) // D3D11, D3D12, XB1, PS4, iOS, macOS, tvOS, glcore, gles3, webgl2.0, Switch
// Real-support for depth-format cube shadow map.
#define SHADOWS_CUBE_IN_DEPTH_TEX
#endif
```
とはいえ結構一般的なグラフィックAPIがそろっているようなので、ほとんどの場合は`float3 vec : TEXCOORD0;`は構造体に追加されないと思っていても良さそう。こればかりは開発対象次第ですね。
ひとまず、`V2F_SHADOW_CASTER;`マクロを展開してみるとこうなりますね。

```
struct v2f {
    //V2F_SHADOW_CASTER;
    float4 pos : SV_POSITION
    float3 vec : TEXCOORD0; //これはグラフィックAPIとSHADOWS_CUBEキーワード次第
};
```

## TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
### 結論
こう展開される

```
        v2f vert( appdata_base v )
        {
            v2f o;
            //TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
            #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
                o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
            #else
                o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); 
                o.pos = UnityApplyLinearShadowBias(o.pos);
            #endif
            return o;
        }
```

### マクロを読む
マクロはこうなっている

```UnityCG.cginc
// Vertex shader part, with support for normal offset shadows. Requires
// position and normal to be present in the vertex input.
#define TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)
```

`TRANSFER_SHADOW_CASTER_NOPOS`を確認してみます。

```UnityCG.cginc
#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    //省略
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    //省略

#else
    //省略
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
        opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
        opos = UnityApplyLinearShadowBias(opos);
    //省略
#endif

```
前述の分岐がここでも出てきます。
前者の分岐先では光源から頂点に向くベクトル（ワールド座標系）と、頂点を`UnityObjectToClipPos()`したクリップ座標を計算していますね。後者の分岐先は、頂点座標と法線を入力に`UnityClipSpaceShadowCasterPos()`と`UnityApplyLinearShadowBias()`でなにやら計算してますね。このふたつの関数を追ってみます。

```UnityCG.cginc
float4 UnityClipSpaceShadowCasterPos(float4 vertex, float3 normal)
{
    float4 wPos = mul(unity_ObjectToWorld, vertex);

    if (unity_LightShadowBias.z != 0.0)
    {
        float3 wNormal = UnityObjectToWorldNormal(normal);
        float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));

        // apply normal offset bias (inset position along the normal)
        // bias needs to be scaled by sine between normal and light direction
        // (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/)
        //
        // unity_LightShadowBias.z contains user-specified normal offset amount
        // scaled by world space texel size.

        float shadowCos = dot(wNormal, wLight);
        float shadowSine = sqrt(1-shadowCos*shadowCos);
        float normalBias = unity_LightShadowBias.z * shadowSine;

        wPos.xyz -= wNormal * normalBias;
    }

    return mul(UNITY_MATRIX_VP, wPos);
}
```
頂点座標、法線をワールド座標に変換して頂点を法線方向に**幾らか**動かしてからクリップ座標に変換していますね。**幾らか**はLightコンポーネントで設定するBias依存っぽい。

`UnityApplyLinearShadowBias()`も確認します。前述のコードから`UnityClipSpaceShadowCasterPos()`で出力された法線方向にオフセットされたクリップ座標が引数にくることが分かっています。

```UnityCG.cginc
float4 UnityApplyLinearShadowBias(float4 clipPos)

{
    // For point lights that support depth cube map, the bias is applied in the fragment shader sampling the shadow map.
    // This is because the legacy behaviour for point light shadow map cannot be implemented by offseting the vertex position
    // in the vertex shader generating the shadow map.
#if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))
    #if defined(UNITY_REVERSED_Z)
        // We use max/min instead of clamp to ensure proper handling of the rare case
        // where both numerator and denominator are zero and the fraction becomes NaN.
        clipPos.z += max(-1, min(unity_LightShadowBias.x / clipPos.w, 0));
    #else
        clipPos.z += saturate(unity_LightShadowBias.x/clipPos.w);
    #endif
#endif

#if defined(UNITY_REVERSED_Z)
    float clamped = min(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#else
    float clamped = max(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
#endif
    clipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);
    return clipPos;
}
```
とりあえずグラフィックAPI差を吸収して深度値の元になる値(clipPos.z)を調整しているっぽい。

なので、展開の結果は以下になる。特別な処理の内容は以下の２点。

- 法線方向に頂点がバイアスでオフセットされる
- 深度がバイアスで調整される


```
        v2f vert( appdata_base v )
        {
            v2f o;
            //TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
            #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
                o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
            #else
                o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); 
                o.pos = UnityApplyLinearShadowBias(o.pos);
            #endif
            return o;
        }
```

## SHADOW_CASTER_FRAGMENT(i)
### 結論
こんな感じに展開される。（少し書きかえました）

```
        float4 frag( v2f i ) : SV_Target
        {
            #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
              //省略
              float depth = (length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w;
              #ifdef UNITY_USE_RGBA_FOR_POINT_SHADOWS
                return EncodeFloatRGBA (min(depth, 0.999));
              #else
                return depth;
              #endif
            #else
              return 0;
            #endif
        }
```

### マクロを追う

振り返っておくと、`SHADOW_CASTER_FRAGMENT(i)`はフラグメントシェーダーで実行されているのでこのマクロが最終的な深度値にあたる値を出力していそうな予想はできる。

```UnityCG.cginc

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    //省略
    #define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
#else
    //省略
    #define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif
```
後者の分岐先は`return 0;`なので特に何もしていなさそう。これは多分`SV_Depth`の値をシャドウマップに使用するから`SV_Target`がどんな値を出力しても関係ないということなんだと思う。前者の分岐先においてだけ`SV_Target`が必要。

```
(length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w
```
の部分ついて考える。
`i.vec`は光源から頂点に向かうベクトルだったので`length(i.vec)`で光源からの距離を計算して、`* _LightPositionRange.w`で距離を0~1に正規化しているのかな。`+ unity_LightShadowBias.x`は距離にバイアスを持たせているだけっぽい。この計算で0~1に収まる深度が計算されているっぽいですね。
`UnityEncodeCubeShadowDepth()`はfloat精度の0~1の値を必要に応じて8bitRGBAにパックしてる。"必要に応じて"というのはfloat精度のレンダリングターゲットをサポートしているか否か(`UNITY_USE_RGBA_FOR_POINT_SHADOWS`キーワード)で分岐されていて、`defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)`ならRGBAにfloatを分割して保存している。
なので以下のように展開される。

```
        float4 frag( v2f i ) : SV_Target
        {
            #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
                //省略
                float depth = (length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w;
                #ifdef UNITY_USE_RGBA_FOR_POINT_SHADOWS
                    return EncodeFloatRGBA (min(depth, 0.999));
                #else
                    return depth;
                #endif
            #else
                return 0;
            #endif
        }
```

## 全体を展開してみる
長くなるので静的分岐ごとに分けて２パターンに展開します。
`#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)`な場合は以下のように展開されるはず。

```
        Tags { "LightMode" = "ShadowCaster" }
        ZWrite On ZTest LEqual Cull Off

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

        struct v2f {
            float4 pos : SV_POSITION;
            float3 vec : TEXCOORD0;
        };

        v2f vert( appdata_base v )
        {
            v2f o;
            o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;
            o.pos = UnityObjectToClipPos(v.vertex);
            return o;
        }

        float4 frag( v2f i ) : SV_Target
        {
            float depth = (length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w;
            
            //defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)の場合はEncodeFloatRGBAでエンコード
            #ifdef UNITY_USE_RGBA_FOR_POINT_SHADOWS
                return EncodeFloatRGBA (min(depth, 0.999));
            #else
                return depth;
            #endif
        }
```

次に、`#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)`**ではない**場合の展開結果。多分多くの場合はこっちが使われると思う。

```
        Tags { "LightMode" = "ShadowCaster" }
        ZWrite On ZTest LEqual Cull Off

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

        struct v2f {
            float4 pos : SV_POSITION;
        };

        v2f vert( appdata_base v )
        {
            v2f o;
            //バイアス依存で法線方向にオフセット+バイアス依存で深度値を調整?
            o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); 
            o.pos = UnityApplyLinearShadowBias(o.pos);
            return o;
        }

        float4 frag( v2f i ) : SV_Target
        {
            return 0;
        }
```

## 備考
`SHADOWS_CUBE`キーワードが有効になる条件だけは確かな情報が見当たらなかったのでわからないまま...
（少なくとも以下のAPIを使用している場合は気にしなくてよい）
`#if defined(SHADER_API_D3D11) || defined(SHADER_API_PSSL) || defined(SHADER_API_METAL) || defined(SHADER_API_GLCORE) || defined(SHADER_API_GLES3) || defined(SHADER_API_VULKAN) || defined(SHADER_API_SWITCH) // D3D11, D3D12, XB1, PS4, iOS, macOS, tvOS, glcore, gles3, webgl2.0, Switch`

と思ったら、以下の記述を見つけたのでpoint lightでrealtime shadowする時にEnableになるのかもしれない。

```AutoLight.cginc
        //point realtime shadow
        #if defined (SHADOWS_CUBE)
       //省略
```

# 影を受ける為に必要な処理を追う
影を受ける側の処理は適切なサンプルになりそうなシェーダーがみつからなかったので負った結果のサンプルを以下に記載します。静的分岐が複雑なのでマクロを追うのがしんどいです。影を受ける側でトリッキーな事をする場面は少ないと思うので深く入り込まなくても良い気はするので深くは調べませんでした。

```
Shader "Unlit/ReceiveShadow"
{
  Properties
  {
    _MainTex ("Texture", 2D) = "white" { }
  }
  SubShader
  {
    Name "FORWARD"
    Tags { "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
    
    Pass
    {
      CGPROGRAM
      
      #pragma vertex vert
      #pragma fragment frag
      #pragma multi_compile_fwdbase_fullshadows
      
      #include "UnityCG.cginc"
      #include "AutoLight.cginc"
      
      struct appdata
      {
        float4 vertex: POSITION;
        float2 uv: TEXCOORD0;
        float2 uv1: TEXCOORD1;
      };
      
      struct v2f
      {
        float2 uv: TEXCOORD0;
        float4 pos: SV_POSITION;
        float3 worldPos: WORLDPOS;
        UNITY_LIGHTING_COORDS(3, 4)
      };
      
      sampler2D _MainTex;
      
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.worldPos = mul(unity_ObjectToWorld, v.vertex);
        UNITY_TRANSFER_LIGHTING(o, v.uv1);
        return o;
      }
      
      fixed4 frag(v2f i): SV_Target
      {
        fixed4 col = tex2D(_MainTex, i.uv);
        UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
        col *= atten;
        return col;
      }
      ENDCG
      
    }
    
    // shadowを受けるのにもshadowcasterパスが必要
    // Pass to render object as a shadow caster
    Pass
    {
      Name "ShadowCaster"
      Tags { "LightMode" = "ShadowCaster" }
      
      ZWrite On ZTest LEqual Cull Off
      
      CGPROGRAM
      
      #pragma vertex vert
      #pragma fragment frag
      #pragma multi_compile_shadowcaster
      #include "UnityCG.cginc"
      
      struct v2f
      {
        float4 pos: SV_POSITION;
      };
      
      v2f vert(appdata_base v)
      {
        v2f o;
        //バイアス依存で法線方向にオフセット+バイアス依存で深度値を調整?
        o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal);
        o.pos = UnityApplyLinearShadowBias(o.pos);
        return o;
      }
      
      float4 frag(v2f i): SV_Target
      {
        return 0;
      }
      ENDCG
      
    }
  }
}

```

## UNITY_LIGHTING_COORDS(n,m)
`v2f`構造体にシャドウマップをサンプリングする為のUV座標を含める必要があります。`UNITY_LIGHTING_COORDS(n,m)`マクロはその為のマクロで以下のように定義されている。

```AutoLight.cginc
#define UNITY_LIGHTING_COORDS(idx1, idx2) DECLARE_LIGHT_COORDS(idx1) UNITY_SHADOW_COORDS(idx2)
```
２つのマクロに分かれていてそれぞれライトコードとシャドウコードを構造体内に宣言してる。
`DECLARE_LIGHT_COORDS(idx1)`も`UNITY_SHADOW_COORDS(idx2)`も、分岐によって2or3or4次元のベクトルを宣言している。変数名は`_LightCoord`と`_ShadowCoord`

## UNITY_TRANSFER_LIGHTING(o, v.uv1);
頂点シェーダー内で`_LightCoord`と`_ShadowCoord`にUV座標を計算し代入する処理が`UNITY_TRANSFER_LIGHTING(o, v.uv1)`マクロで提供されています。`uv1`と書いているのはStandardシェーダーないでそう書かれていたのを持ってきたからなんですが、lightmap uvを使いたいとかそういうことなのかな。あんまりその辺理解していないのでよく分からず...
考えやすい様にマクロで使用されている変数名は書き換えてますが、定義は以下のようになっています。

```AutoLight.cginc
#define UNITY_TRANSFER_LIGHTING(o, coord) COMPUTE_LIGHT_COORDS(o) UNITY_TRANSFER_SHADOW(o, coord)
```

`COMPUTE_LIGHT_COORDS(o)`はライトの種類で分岐してる。が、基本的にライトをカメラとして考えてカメラから見たクリップ座標のような座標を算出している感じになっている。例えば以下のような感じ。厳密にどういう変換をしているかは`unity_WorldToLight`行列次第。

```AutoLight.cginc
#define COMPUTE_LIGHT_COORDS(o) o._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, v.vertex)).xyz;
```

`UNITY_TRANSFER_SHADOW(o, coord)`は分岐パターンが多くて追うのが億劫なので中止。とりあえず、`o._ShadowCoord`変数に2ro3or4次元のベクトルが代入される。計算に使われる特別な変数は`unity_WorldToShadow[0]`,`_LightPositionRange.xyz`,`unity_LightmapST.xyzw`くらい。

## UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);
後はフラグメントシェーダーでシャドウマップを参照して影を付けるだけ。
`UNITY_LIGHT_ATTENUATION`マクロも分岐が複雑なのでざっくりの理解で進める。例えばこんな分岐がある。
シャドウとかCoolieとかを含めて諸々処理した結果を`fixed destName`変数に代入している。
```
#define UNITY_LIGHT_ATTENUATION(destName, input, worldPos) \
    DECLARE_LIGHT_COORD(input, worldPos); \
    fixed shadow = UNITY_SHADOW_ATTENUATION(input, worldPos); \
    fixed destName = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
```

どの分岐でも`UNITY_SHADOW_ATTENUATION(input, worldPos);`は含まれていて、これは`UnityComputeForwardShadows()`関数を呼ぶか、`SHADOW_ATTENUATION(a)`マクロを実行してる。ほとんどの場合`UnityComputeForwardShadows()`関数が呼ばれている。

```AutoLight.cginc
half UnityComputeForwardShadows(float2 lightmapUV, float3 worldPos, float4 screenPos)
{
    //fade value
    float zDist = dot(_WorldSpaceCameraPos - worldPos, UNITY_MATRIX_V[2].xyz);
    float fadeDist = UnityComputeShadowFadeDistance(worldPos, zDist);
    half  realtimeToBakedShadowFade = UnityComputeShadowFade(fadeDist);

    //baked occlusion if any
    half shadowMaskAttenuation = UnitySampleBakedOcclusion(lightmapUV, worldPos);

    half realtimeShadowAttenuation = 1.0f;
    //directional realtime shadow
    #if defined (SHADOWS_SCREEN)
        #if defined(UNITY_NO_SCREENSPACE_SHADOWS) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)
            realtimeShadowAttenuation = unitySampleShadow(mul(unity_WorldToShadow[0], unityShadowCoord4(worldPos, 1)));
        #else
            //Only reached when LIGHTMAP_ON is NOT defined (and thus we use interpolator for screenPos rather than lightmap UVs). See HANDLE_SHADOWS_BLENDING_IN_GI below.
            realtimeShadowAttenuation = unitySampleShadow(screenPos);
        #endif
    #endif

    #if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)
    //avoid expensive shadows fetches in the distance where coherency will be good
    UNITY_BRANCH
    if (realtimeToBakedShadowFade < (1.0f - 1e-2f))
    {
    #endif

        //spot realtime shadow
        #if (defined (SHADOWS_DEPTH) && defined (SPOT))
            #if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)
                unityShadowCoord4 spotShadowCoord = mul(unity_WorldToShadow[0], unityShadowCoord4(worldPos, 1));
            #else
                unityShadowCoord4 spotShadowCoord = screenPos;
            #endif
            realtimeShadowAttenuation = UnitySampleShadowmap(spotShadowCoord);
        #endif

        //point realtime shadow
        #if defined (SHADOWS_CUBE)
            realtimeShadowAttenuation = UnitySampleShadowmap(worldPos - _LightPositionRange.xyz);
        #endif

    #if defined(UNITY_FAST_COHERENT_DYNAMIC_BRANCHING) && defined(SHADOWS_SOFT) && !defined(LIGHTMAP_SHADOW_MIXING)
    }
    #endif

    return UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, realtimeToBakedShadowFade);
}
```

`return UnityMixRealtimeAndBakedShadows(...)`とあるのでこの関数内でシャドウの処理をまとめて行っている。で、フラグメントシェーダーまで巻き戻ると`UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);`という記述の中で上記の関数が呼ばれなんやかんや影の処理が行われた結果がフラグメントシェーダー上で`fixed atten;`変数として宣言された状態になる。この`atten`変数をフラグメントシェーダーの結果に乗算すれば影が落ちたような状態になる。その後にEmissionを加算すればOK。長ぁ。
余談ですが、Standardシェーダーでは`FragmentGI()`関数に`atten`を引数として渡していて中でグローバルイルミネーションのライトカラーに`atten`が乗算されるようになっている。

## 備考
### 変数名の制約
`UNITY_LIGHT_ATTENUATION`が内部で`SV_POSITION`セマンティクスをバインドした変数を使っています。そのコードでは変数名が`pos`とされている前提で書かれているのでそれに合わせる必要があります。

### もっと簡潔なReceive Shadow
Standardシェーダーの処理を追って調べていたので`UNITY_LIGHTING_COORDS`マクロを使いましたが、[ArktoonShader](https://github.com/synqark/Arktoon-Shaders)のコードを追ってみたところ、以下のように`UNITY_LIGHTING_COORDS`を`SHADOW_COORDS`に、`UNITY_TRANSFER_LIGHTING`を`TRANSFER_SHADOW`にしても影を受けられるようです。

```
      struct v2f
      {
        float2 uv: TEXCOORD0;
        float4 pos: SV_POSITION;
        float3 worldPos: WORLDPOS;
        //UNITY_LIGHTING_COORDS(3, 4)
        SHADOW_COORDS(3)
      };
      
      sampler2D _MainTex;
      
      v2f vert(appdata v)
      {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        o.worldPos = mul(unity_ObjectToWorld, v.vertex);
        //UNITY_TRANSFER_LIGHTING(o, v.uv1);
        TRANSFER_SHADOW(o);
        return o;
      }
```

たしかに、`UNITY_LIGHTING_COORDS`は`_LightCoord`と`_ShadowCoord`をまとめて扱っているからシャドウだけに絞ればそれでいいのか。なるほど:bow: