知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】オブジェクトの影を落とす #107

前回の成果

Bloomシェーダーについて学んだ。

soramamenatan.hatenablog.com


今回やること

シェーダーでの影について学んでいきます。


事前準備

Scene上にCubeを配置します。
Cubeの影が出るようにPlaneを配置して準備は完了となります。

f:id:soramamenatan:20210612151136p:plain


Unityのデフォルトのマテリアルを使用する際には特に意識せずとも、オブジェクトの影が表示されています。
ですが、自分でシェーダーを使用し、そのシェーダーを元にしたマテリアルを使用すると影の表示がなくなってしまいます。

影が出ない例

UnlitShaderを制作し、そのマテリアルをアタッチしています。
Planeに影が出ていません。

f:id:soramamenatan:20210612151443p:plain

なので、シェーダーで影を描画していきます。


シェーダー

Shader "Unlit/ShadowDef" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {

        // 通常の描画
        Pass {
            Tags { "RenderType"="Opaque" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv);
                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 uv : 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
        }
    }
}

1Pass目でオブジェクトの描画をし、2Pass目で影の描画をしていきます。
影自体の箇所は短いのですが、組み込みの関数を使用しているので中身について解説していきます。


LightMode Tag

以下の箇所で使用しています。

Tags { "LightMode"="ShadowCaster" }

RenderTypeQueueと同じTagになります。
このタグは、ライティングに使用されるTagになっています。
組み込みのレンダリングパイプラインで、LightModeTagを設定しない場合には、Unityはライティングや影なしで描画しようとします。
ですので、UnlitShaderだと影やライティングが描画されません。
今回は、ShadowCasterを指定しているので、オブジェクトの深度をシャドウマップから深度テクスチャにレンダリングすることをUnityに伝えています。

LightMode Tagの一例

Tag名 意味
Always 常にレンダリングされ、ライティングは適応されない
デフォルト値
ForwardBase Forwardレンダリングで使用
アンビエント、メインのDirectionalLight、Vertex/SHLight、ライトマップを適応
Deferred DeferredShading(遅延シェーディング)で使用
g-Bufferをレンダリング
ShadowCaster オブジェクトの深度をシャドウマップから深度テクスチャにレンダリング

他にもLightMode Tagは種類があるのですが、一部割愛させていただきます。
以下リファレンスに他の種類が記載されています。

docs.unity3d.com


V2F_SHADOW_CASTER

以下の箇所で使用しています。

struct v2f {
    V2F_SHADOW_CASTER;
};

V2F_SHADOW_CASTERの定義

#define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS UNITY_POSITION(pos)

V2F_SHADOW_CASTER_NOPOSのあとにUNITY_POSITION(pos)を呼んでいるのと同義です。
更に展開します。

V2F_SHADOW_CASTER_NOPOSの定義

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    // Rendering into point light (cubemap) shadows
    #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);

#else
    // Rendering into directional or spot light shadows
    #define V2F_SHADOW_CASTER_NOPOS
    // Let embedding code know that V2F_SHADOW_CASTER_NOPOS is empty; so that it can workaround
    // empty structs that could possibly be produced.
    #define V2F_SHADOW_CASTER_NOPOS_IS_EMPTY
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) \
        opos = UnityObjectToClipPos(v.vertex.xyz); \
        opos = UnityApplyLinearShadowBias(opos);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
        opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
        opos = UnityApplyLinearShadowBias(opos);
    #define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif

V2F_SHADOW_CASTER_NOPOSの解説

defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)

は、シャドウマップにキューブマップを使うポイントライトかを見ているifになります。

また、SHADOWS_CUBEはLightコンポーネントのTypeとShadowTypeによって定義されているものになります。

Lightコンポーネント

f:id:soramamenatan:20210613113832p:plain

もう片方の条件となっているSHADOWS_CUBE_IN_DEPTH_TEXの定義は以下になります。

SHADOWS_CUBE_IN_DEPTH_TEXの定義
#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

基本的なプラットフォームは記載されているので、あまり気にする必要はなさそうです。


UNITY_POSITION(pos)の定義

// On D3D reading screen space coordinates from fragment shader requires SM3.0
#define UNITY_POSITION(pos) float4 pos : SV_POSITION

こちらは単純で、SV_POSITIONの定義だけになります。

V2F_SHADOW_CASTER_NOPOSのまとめ

少し長くなってしまったので、まとめます。

深度テクスチャではないキューブマップ
float3 vec : TEXCOORD0;
float4 pos : SV_POSITION
それ以外(基本的にはこちら)
float4 pos : SV_POSITION


TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)

以下の箇所で使用しています。

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

TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)の定義

TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)に置き換わっているだけとなります。

// 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の定義

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    // Rendering into point light (cubemap) shadows
    #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);

#else
    // Rendering into directional or spot light shadows
    #define V2F_SHADOW_CASTER_NOPOS
    // Let embedding code know that V2F_SHADOW_CASTER_NOPOS is empty; so that it can workaround
    // empty structs that could possibly be produced.
    #define V2F_SHADOW_CASTER_NOPOS_IS_EMPTY
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) \
        opos = UnityObjectToClipPos(v.vertex.xyz); \
        opos = UnityApplyLinearShadowBias(opos);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
        opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
        opos = UnityApplyLinearShadowBias(opos);
    #define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif

最初の条件式はV2F_SHADOW_CASTERと同じく深度テクスチャではないキューブマップかを見ています。

TRANSFER_SHADOW_CASTER_NOPOSの解説(深度テクスチャではないキューブマップ)

レガシーな方は割愛させていただきます。

定義
#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);

_LightPositionRangeの定義はこちらです。

float4 _LightPositionRange; // xyz = pos, w = 1/range

つまり、TRANSFER_SHADOW_CASTER_NOPOSはこうなります。

o.vec = 頂点のワールド座標 - ライトの座標
opos = 頂点のクリップ座標

TRANSFER_SHADOW_CASTER_NOPOSの解説(それ以外の場合)

深度テクスチャではないキューブマップ以外の場合になります。
こちらもレガシーなものは割愛します。

定義
#define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
  opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
  opos = UnityApplyLinearShadowBias(opos);

UnityClipSpaceShadowCasterPos(v.vertex, v.normal)
UnityApplyLinearShadowBias(opos)に展開されて処理されています。

UnityClipSpaceShadowCasterPos

定義
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);
}
// Legacy, not used anymore; kept around to not break existing user shaders
float4 UnityClipSpaceShadowCasterPos(float3 vertex, float3 normal)
{
    return UnityClipSpaceShadowCasterPos(float4(vertex, 1), normal);
}

unity_LightShadowBias.zはユーザーが指定した法線のオフセット量になります。
基本的には、オブジェクトのクリップ座標を計算しているものになります。


法線方向にnormalBiasを乗算しているのは、シャドウアクネを軽減させるためになります。
このバイアスのスケールは法線とライトのアークコサインのサインに比例します。
ですので、

float shadowSine = sqrt(1-shadowCos*shadowCos);

で求めています。
このバイアスの意味合いは以下サイト様に記載されています。

Shadow Mapping Summary – Part 1 – The Witness


シャドウアクネ

少し脱線してしまいますが、シャドウアクネについて解説します。

以下の画像でシャドウアクネが発生していることが確認できます。
オブジェクトの影が描画されているのですが、影の他に縦方向の縞模様のようなものができてしまっています。
それがシャドウアクネと呼ばれるものになります。

f:id:soramamenatan:20210613121053j:plain 影 - Unity マニュアル:より引用


発生の原因

シャドウマップで指定された距離のピクセルが遠くにあるように計算されてしまう場合に発生します。


以下の画像のように本来ポリゴンは頂点AからBにかけてなめらかになっています。
それがシャドウマップのテクセルによってZ値でまとめられ、一定の間隔で区切られてしまいます。

f:id:soramamenatan:20210613121459p:plain ☆PROJECT ASURA☆ [Direct3D 11] 『シャドウマッピングの基本』:より引用

シャドウマップを使用し影をつけることは、この一定間隔に区切られたポリゴンとの比較をすることになります。
つまり以下画像の緑色の箇所が影と判定されてしまいます。
なので縞模様ができてしまっています。

f:id:soramamenatan:20210613121502p:plain ☆PROJECT ASURA☆ [Direct3D 11] 『シャドウマッピングの基本』:より引用

対策として最も良いのは、1テクセルの大きさを小さくしてシャドウマップの解像度を上げることになります。
ですがこの方法ですとパフォーマンスが著しく落ちてしまいます。
なので、バイアスをかけてあげることによって対策をしています。

f:id:soramamenatan:20210613121506p:plain ☆PROJECT ASURA☆ [Direct3D 11] 『シャドウマッピングの基本』:より引用

ただし、バイアスをかけすぎてしまうとオブジェクトと影が離れてしまうピーターパン現象と呼ばれるものが発生します。

ピーターパン現象の例

f:id:soramamenatan:20210613121958j:plain 影 - Unity マニュアル:より引用


試してみる

実際にUnity上で試してみたいと思います。

特になにも考えずにオブジェクトをScene上に配置します。
違和感なく影が出ているのがわかるかと思います。

通常の影

f:id:soramamenatan:20210613122132p:plain

LightコンポーネントBiasNormalBiasの値を0にしてみます。
そうすると、オブジェクトの影自体は問題なく描画されていますが、Planeに縞模様ができてしまっています。

シャドウアクネ

f:id:soramamenatan:20210613122146p:plain

逆にLightコンポーネントBiasNormalBiasの値を最大値にしてみます。
そうすると、オブジェクトと影との距離が離れてしまっています。

ピーターパン現象

オブジェクトの大きさがそのままだと現象が発生していることが確認しにくかったので、縦方向にスケールさせています。

f:id:soramamenatan:20210613122159p:plain


UnityApplyLinearShadowBiasの定義

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;
}
#if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))

で分岐されていますが、UnityApplyLinearShadowBiasを呼ぶ際にこの条件式を通らないと呼ばないので、分岐の中は実行されています。

UnityApplyLinearShadowBiasの解説

unity_LightShadowBias .xunity_LightShadowBias .yにはこのブログを書いている段階では情報がありませんでした。
ですが、おそらくLightコンポーネントBiasの値を元に処理をしているものだと思われます。

Bias

f:id:soramamenatan:20210613123237p:plain

深度値のものであるZをバイアスに応じて増加させて、その値をClampしています。


UNITY_REVERSED_Z

UNITY_REVERSED_Zはプラットフォーム毎のZの向きに対応するものになります。

UNITY_REVERSED_Zの値 プラッフォーム Zバッファの範囲
1 DX11/12
PS4
XboxOne
Metal
1 ~ 0
0 その他のプラットフォーム 0 ~ 1

TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)のまとめ

こちらもまとめます。

深度テクスチャではないキューブマップ
o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;
o.pos = UnityObjectToClipPos(v.vertex);
それ以外(基本的にはこちら)
o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal);
o.pos = UnityApplyLinearShadowBias(o.pos);


SHADOW_CASTER_FRAGMENT

使用箇所は以下になります。

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

SHADOW_CASTER_FRAGMENTの定義

#if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
    // Rendering into point light (cubemap) shadows
    #define V2F_SHADOW_CASTER_NOPOS float3 vec : TEXCOORD0;
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz; opos = UnityObjectToClipPos(v.vertex);
    #define SHADOW_CASTER_FRAGMENT(i) return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);

#else
    // Rendering into directional or spot light shadows
    #define V2F_SHADOW_CASTER_NOPOS
    // Let embedding code know that V2F_SHADOW_CASTER_NOPOS is empty; so that it can workaround
    // empty structs that could possibly be produced.
    #define V2F_SHADOW_CASTER_NOPOS_IS_EMPTY
    #define TRANSFER_SHADOW_CASTER_NOPOS_LEGACY(o,opos) \
        opos = UnityObjectToClipPos(v.vertex.xyz); \
        opos = UnityApplyLinearShadowBias(opos);
    #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos) \
        opos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal); \
        opos = UnityApplyLinearShadowBias(opos);
    #define SHADOW_CASTER_FRAGMENT(i) return 0;
#endif

ここも他と同様に深度テクスチャではないキューブマップとそれ以外を見ています。
それ以外の場合は0を返しています。
これは深度を利用しているので、色は0で問題ないからになります。

UnityEncodeCubeShadowDepth

定義
// Encoding/decoding [0..1) floats into 8 bit/channel RGBA. Note that 1.0 will not be encoded properly.
inline float4 EncodeFloatRGBA( float v )
{
    float4 kEncodeMul = float4(1.0, 255.0, 65025.0, 16581375.0);
    float kEncodeBit = 1.0/255.0;
    float4 enc = kEncodeMul * v;
    enc = frac (enc);
    enc -= enc.yzww * kEncodeBit;
    return enc;
}

float4 UnityEncodeCubeShadowDepth (float z)
{
    #ifdef UNITY_USE_RGBA_FOR_POINT_SHADOWS
    return EncodeFloatRGBA (min(z, 0.999));
    #else
    return z;
    #endif
}

floatの精度を必要に応じて、rgbaに入れています。
必要なプラットフォームの分岐はUNITY_USE_RGBA_FOR_POINT_SHADOWSで見ています。
これは、

// SHADER_API_GLES : OpenGL ES 2.0
// SHADER_API_GLES3 : OpenGL ES 3.0/3.1
defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)

の場合となります。

SHADOW_CASTER_FRAGMENTのまとめ

深度テクスチャではないキューブマップ
return UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
それ以外(基本的にはこちら)
return 0


シェーダーにコメントを付与

全体のまとめとして、コメントに記載しました。

Shader "Unlit/ShadowDef" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader {

        // 通常の描画
        Pass {
            Tags { "RenderType"="Opaque" }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv);
                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 uv : TEXCOORD0;
            };

            struct v2f {
                // 深度テクスチャではないキューブマップの場合
                // float3 vec : TEXCOORD0;
                // float4 pos : SV_POSITION
                // それ以外の場合
                // float4 pos : SV_POSITION
                V2F_SHADOW_CASTER;
            };

            v2f vert (appdata v) {
                v2f o;
                // 深度テクスチャではないキューブマップの場合
                // o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;
                // o.pos = UnityObjectToClipPos(v.vertex);
                // それ以外の場合
                // o.pos = UnityClipSpaceShadowCasterPos(v.vertex, v.normal);
                // o.pos = UnityApplyLinearShadowBias(o.pos);
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                // 深度テクスチャではないキューブマップの場合
                // UnityEncodeCubeShadowDepth ((length(i.vec) + unity_LightShadowBias.x) * _LightPositionRange.w);
                // それ以外の場合
                // return 0
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }
}


結果

右が通常のオブジェクト、左が今回制作したシェーダーのオブジェクトになります。
左のオブジェクトでも影がPlaneに落ちていることが確認できます。

f:id:soramamenatan:20210613135346p:plain


今回は以上となります。
ここまでご視聴ありがとうございました。


参考サイト様

www.project-asura.com

qiita.com

UnityでForwardのライトに対応したLambert反射モデルのシェーダを作成する | 測度ゼロの抹茶チョコ