【Unity】【Shader】影を描画する、影を受け取る

# はじめに

例えばDiffuseのShaderを自作すると、それだけでは影が描画されない。影の実装も自分で行う必要があるので、それをやってみる。

<img width="941" alt="Screen Shot 2020-01-15 at 18.17.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/8d128464-30dd-976d-a176-3623c8339763.png">

# 周りのオブジェクトに影を描画する

まずは、自身が周りのオブジェクトから影を受けるようにする。

## Passが二つ

今回の方法では、```Pass{}```を二つ記述する必要がある。一つ目のPassでオブジェクトの色を描画、二つ目のPassで影を描画することになる。

## "LightMode"="ShadowCaster"

LightModeタグはそのPassが光を描画する中で、どんな役割を担うかを定義する。

> LightMode tag defines Pass’ role in the lighting pipeline.

影を描画する場合は、```Tags{ "LightMode"="ShadowCaster" }```とする。これによって、周りのオブジェクトに影を描画するようになる。

> ShadowCaster: Renders object depth into the shadowmap or a depth texture.

[ShaderLab: Pass Tags](https://docs.unity3d.com/Manual/SL-PassTags.html)

## multi_compile_shadowcasterを追加

```#pragma multi_compile_shadowcaster```を追加する。これでコンパイラに影を描画したいって伝えるらしい？＊要確認

## マクロを使用する

実際の描画処理はマクロを呼び出すだけで完了する。使うのは以下の３つ。

```
V2F_SHADOW_CASTER
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
SHADOW_CASTER_FRAGMENT(i)
```

## 結果

無事に影が描画されるようになりました。

<img width="534" alt="Screen Shot 2020-01-15 at 18.24.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/2c5c2dd1-94ac-9adc-038e-8b0d9fc7bd94.png">

## ここまでのコード

```.glsl
Pass
{
    // 一つ目のパス。オブジェクト自身を描画する。
}
Pass
 {
     Tags{ "LightMode"="ShadowCaster" }
     
     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag
     #pragma multi_compile_shadowcaster
     
     #include "UnityCG.cginc"
     
     struct appdata
     {
         float4 vertex : POSITION;
         float3 normal : NORMAL;
         float2 texcoord : TEXCOORD0;
     };
     
     struct v2f
     {
         V2F_SHADOW_CASTER;
     };
     
     v2f vert (appdata v)
     {
         v2f o;
         TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
         return o;
     }
     
     fixed4 frag (v2f i) : SV_Target
     {
         SHADOW_CASTER_FRAGMENT(i)
     }
     ENDCG
 }
```

# 影を受け取る

ここまでの方法では自身が他のオブジェクトの影を受け取ることが出来ない。次にそこを改善してみる。

## ひとつめのPassを変更する

他オブジェクトに影を落とすために二つ目のPassを記述したが、自身が影を受け取る場合は、ひとつめのPassを変更する。

## pragmaを追加

もうなんの事か分からないけど、これを追加するらしい

```#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight```

## includeも追加

これも何の事か分かってないけど追加する

```
#include "Lighting.cginc" 
#include "AutoLight.cginc"
```

[Vertex and fragment shader examples](https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html)

## マクロを追加

```SHADOW_COORDS, TRANSFER_SHADOW, SHADOW_ATTENUATION```を使用して影を計算する。
```TRANSFER_SHADOW```にv2fを渡す際はvertexという名前を受け付けないので、posに変更する。
以下のコードにコメントしてある箇所。

## 結果

無事に影を受け取ることができた。

<img width="846" alt="Screen Shot 2020-01-16 at 8.35.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85509/2fb1e546-099b-5fcf-bf11-1ba9a0692531.png">


# 今回のコード

```.glsl
Shader "Unlit/ShadowVF"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags { "LightMode"="ForwardBase" }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            #include "UnityCG.cginc" 
            #include "UnityLightingCommon.cginc"
            #include "Lighting.cginc" 
            #include "AutoLight.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                fixed4 diff : COLOR0;
                float4 pos : SV_POSITION; // posに変更する！！TRANSFER_SHADOWがposとうい名前でないと受け付けない。
                SHADOW_COORDS(1)
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                // ここの左辺もposに変更
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half NdotL = saturate(dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = NdotL * _LightColor0;
                TRANSFER_SHADOW(o)
                
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // 影を計算
                fixed4 shadow = SHADOW_ATTENUATION(i);
                col *= i.diff * shadow;
                return col;
            }
            ENDCG
        }
        
        Pass
        {
            Tags{ "LightMode"="ShadowCaster" }
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };
            
            struct v2f
            {
                V2F_SHADOW_CASTER;
            };
            
            v2f vert (appdata v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
        
    }
}

```