Can't get point/spot shadows working on grass shader

Hey,

I’m working on a grass shader that creates the grass with a geometry shader. I’ve got directional lighting and shadows working, alongside ambient lighting and light attenuation for spot/point lights.

However, I can’t figure out how to get shadows cast by spot/point lights to render on the grass.

Here’s the full shader code, I would be very grateful for any help.

Shader "GW/Foliage/Grass"
{
    Properties
    {
        _BottomColor("Bottom Color", Color) = (0,1,0,1)
        _TopColor("Top Color", Color) = (1,1,0,1)
        _GrassHeight("Grass Height", Float) = 1
        _GrassWidth("Grass Width", Float) = 0.06
        _RandomHeight("Grass Height Randomness", Float) = 0.25
        _WindSpeed("Wind Speed", Float) = 100
        _WindStrength("Wind Strength", Float) = 0.05
        _Rad("Blade Radius", Range(0,1)) = 0.6
        _BladeForward("Blade Forward Amount", Float) = 0.38
        _BladeCurve("Blade Curvature Amount", Range(1, 4)) = 2
        _AmbientStrength("Ambient Strength",  Range(0,1)) = 0.5
        _MinDist("Min Distance", Float) = 40
        _MaxDist("Max Distance", Float) = 60
    }

    CGINCLUDE

    #include "UnityCG.cginc"
    #include "Lighting.cginc"
    #include "AutoLight.cginc"
    #include "UnityLightingCommon.cginc"
    #pragma multi_compile_fwdbase_fullforwardshadows
    #pragma multi_compile_fog
    #define GrassSegments 5
    #define GrassBlades 4

    struct v2g
    {
        float4 pos : SV_POSITION;
        float3 norm : NORMAL;
        float2 uv : TEXCOORD0;
    };

    struct g2f
    {
        float4 pos : SV_POSITION;
        float3 norm : NORMAL;
        float2 uv : TEXCOORD0;
        float3 worldPos : TEXCOORD3;
        LIGHTING_COORDS(5, 6)
        UNITY_FOG_COORDS(4)
    };

    half _GrassHeight;
    half _GrassWidth;
    half _WindSpeed;
    float _WindStrength;
    half _Radius, _Strength;
    float _Rad;

    float _RandomHeight;
    float _BladeForward;
    float _BladeCurve;

    float _MinDist, _MaxDist;

    uniform float3 _PositionMoving;

    v2g vert(appdata_full input)
    {
        float3 v0 = input.vertex.xyz;

        v2g output;
        output.pos = input.vertex;
        output.norm = input.normal;
        output.uv = input.texcoord;
        return output;
    }

    float rand(float3 co)
    {
        return frac(sin(dot(co.xyz, float3(12.9898, 78.233, 53.539))) * 43758.5453);
    }

    // https://gist.github.com/keijiro/ee439d5e7388f3aafc5296005c8c3f33
    float3x3 AngleAxis3x3(float angle, float3 axis)
    {
        float c, s;
        sincos(angle, s, c);

        float t = 1 - c;
        float x = axis.x;
        float y = axis.y;
        float z = axis.z;

        return float3x3(
            t * x * x + c, t * x * y - s * z, t * x * z + s * y,
            t * x * y + s * z, t * y * y + c, t * y * z - s * x,
            t * x * z - s * y, t * y * z + s * x, t * z * z + c
            );
    }

    struct unityTransferVertexToFragmentHack
    {
        float3 vertex : POSITION;
    };

    g2f GrassVertex(float3 vertexPos, float width, float height, float offset, float curve, float2 uv, float3x3 rotation, float3 faceNormal, float3 worldPos) {
        g2f OUT;
        OUT.pos = UnityObjectToClipPos(vertexPos + mul(rotation, float3(width, height, curve) + float3(0, 0, offset)));
        OUT.norm = faceNormal;
        OUT.uv = uv;
        OUT.worldPos = worldPos;

        // send extra vertex to forwardadd pass
        unityTransferVertexToFragmentHack v;
        v.vertex = vertexPos + mul(rotation, float3(width, height, curve) + float3(0, 0, offset));
        TRANSFER_VERTEX_TO_FRAGMENT(OUT);
        UNITY_TRANSFER_FOG(OUT, OUT.pos);

        return OUT;
    }

    [maxvertexcount(51)]
    void geom(point v2g IN[1], inout TriangleStream<g2f> triStream)
    {
        float forward = rand(IN[0].pos.yyz) * _BladeForward;
        float3 lightPosition = _WorldSpaceLightPos0;

        float3 perpendicularAngle = float3(0, 0, 1);
        float3 faceNormal = cross(perpendicularAngle, IN[0].norm) * lightPosition;

        float4 worldPos = mul(unity_ObjectToWorld, IN[0].pos);

        float distanceFromCamera = distance(worldPos, _WorldSpaceCameraPos);
        float distanceFade = 1 - saturate((distanceFromCamera - _MinDist) / _MaxDist);

        float3 v0 = IN[0].pos.xyz;

        float3 wind1 = float3(sin(_Time.x * _WindSpeed + v0.x) + sin(_Time.x * _WindSpeed + v0.z * 2) + sin(_Time.x * _WindSpeed * 0.1 + v0.x), 0,
            cos(_Time.x * _WindSpeed + v0.x * 2) + cos(_Time.x * _WindSpeed + v0.z));

        wind1 *= _WindStrength;

        _GrassHeight *= clamp(rand(IN[0].pos.xyz), 1 - _RandomHeight, 1 + _RandomHeight);

        for (int j = 0; j < (GrassBlades * distanceFade); j++)
        {
            float3x3 facingRotationMatrix = AngleAxis3x3(rand(IN[0].pos.xyz) * UNITY_TWO_PI + j, float3(0, 1, -0.1));
            float3x3 transformationMatrix = facingRotationMatrix;
            float radius = j / (float)GrassBlades;
            float offset = (1 - radius) * _Rad;
            for (int i = 0; i < GrassSegments; i++)
            {
                float t = i / (float)GrassSegments;
                float segmentHeight = _GrassHeight * t;
                float segmentWidth = _GrassWidth * (1 - t);

                segmentWidth = i == 0 ? _GrassWidth * 0.3 : segmentWidth;

                float segmentForward = pow(t, _BladeCurve) * forward;

                float3x3 transformMatrix = i == 0 ? facingRotationMatrix : transformationMatrix;

                float3 newPos = i == 0 ? v0 : v0 + (wind1 * t);

                triStream.Append(GrassVertex(newPos, segmentWidth, segmentHeight, offset, segmentForward, float2(0, t), transformMatrix, faceNormal, worldPos));
                triStream.Append(GrassVertex(newPos, -segmentWidth, segmentHeight, offset, segmentForward, float2(1, t), transformMatrix, faceNormal, worldPos));
            }

            triStream.Append(GrassVertex(v0 + wind1, 0, _GrassHeight, offset, forward, float2(0.5, 1), transformationMatrix, faceNormal, worldPos));
            triStream.RestartStrip();
        }
    }

    ENDCG

    SubShader
    {
        Cull Off

        Pass
        {
            Tags
            {
                "RenderType" = "Geometry"
                "LightMode" = "ForwardBase"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma geometry geom
            #pragma target 4.6
            #pragma multi_compile DIRECTIONAL POINT SPOT SHADOWS_DEPTH SHADOWS_SCREEN

            float4 _TopColor;
            float4 _BottomColor;
            float _AmbientStrength;

            float4 frag(g2f input) : SV_Target
            {
                UNITY_LIGHT_ATTENUATION(atten, input, input.worldPos);

#ifdef POINT
                unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(input.worldPos, 1)).xyz;
                fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
                atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow;
#elif defined(SPOT)
                DECLARE_LIGHT_COORD(input, input.worldPos);
                fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
                atten = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
#endif

                float4 baseColor = lerp(_BottomColor, _TopColor, saturate(input.uv.y));
                float3 ambient = ShadeSH9(float4(0, 1, 0, 1.0));
                float4 final = baseColor * atten * _LightColor0;
                final.rgb += (ambient * baseColor);
                return final;
            }

            ENDCG
        }
       
        Pass
        {
            Tags
            {
                "LightMode" = "ForwardAdd"
            }

            Blend One One
            ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag                                   
            #pragma multi_compile DIRECTIONAL POINT SPOT SHADOWS_DEPTH SHADOWS_SCREEN

            float4 _TopColor;
            float4 _BottomColor;

            float4 frag(g2f input) : SV_Target
            {
                UNITY_LIGHT_ATTENUATION(atten, input, input.worldPos);
#ifdef POINT
                unityShadowCoord3 lightCoord = mul(unity_WorldToLight, unityShadowCoord4(input.worldPos, 1)).xyz;
                fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
                atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).r * shadow;
#elif defined(SPOT)
                DECLARE_LIGHT_COORD(input, input.worldPos);
                fixed shadow = UNITY_SHADOW_ATTENUATION(input, input.worldPos);
                atten = (lightCoord.z > 0) * UnitySpotCookie(lightCoord) * UnitySpotAttenuate(lightCoord.xyz) * shadow;
#endif

                float4 baseColor = lerp(_BottomColor, _TopColor, saturate(input.uv.y));
                float3 ambient = ShadeSH9(float4(0, 1, 0, 1.0));
                float4 final = baseColor * atten * _LightColor0;
                final.rgb += (ambient * baseColor);
                return final;
            }
            ENDCG
        }

        Pass
        {
            Tags
            {
                "LightMode" = "ShadowCaster"
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag
            #pragma multi_compile_shadowcaster

            float4 frag(g2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
    }   
    Fallback "VertexLit"
}

Bumping, still haven’t figured this out.