URP Recieve shadow for custom unlit shader.

Using Unity 2021.3.6f1 and URP 12.1.7 with a custom unlit shader.

I’m trying to add shadows to my gpu instanced grass but I can’t get it working. I’ve already figured out how I make the objcets cast shadows but they can’t recieve any.
I’ve been stuck here for several days now and can’t find any solutions (that work for me) on the web or in the Unity documentation.

I came across posts like these: Water shader graph: Transparency and shadows / universal render pipeline order
But this does not work either. I don’t get any erros, just an transparent object that does not cast shadows.

Hope someone can help.

Here is my full shader code so far that is able to cast shadows:

Shader "Unlit/GrassBladeIndirect"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}
        _RenderTex ("Render Tex", 2D) = "white" {}
        _PrimaryCol ("Primary Color", Color) = (1, 1, 1)
        _SecondaryCol ("Secondary Color", Color) = (1, 0, 1)
        _AOColor ("AO Color", Color) = (1, 0, 1)
        _TipColor ("Tip Color", Color) = (0, 0, 1)
        _Scale ("Scale", Range(0.0, 2.0)) = 0.0
        _MeshDeformationLimit ("Mesh Deformation Limit", Range(0.0, 5.0)) = 0.0
        _WindNoiseScale ("Wind Noise Scale", float) = 0.0
        _WindStrength ("Wind Strength", float) = 1.0
        _WindSpeed ("Wind Speed", Vector) = (0, 0, 0, 0)
    }
    SubShader
    {
        LOD 100
        Cull Off
        ZWrite On
        Name "Grass"
        Tags {
            "RenderType"="Opaque"
            "RenderPipeline"="UniversalPipeline"
            "LightMode"="UniversalForwardOnly"
        }

        Pass
        {

            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForwardOnly" }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            #pragma target 4.5

            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _SHADOWS_SOFT
            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

             //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNoise_RandomValue_float (float2 uv)
            {
                  return frac(sin(dot(uv, float2(12.9898, 78.233)))*43758.5453);
            }

            //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNnoise_Interpolate_float (float a, float b, float t)
            {
                 return (1.0-t)*a + (t*b);
            }
          
             //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNoise_ValueNoise_float (float2 uv)
            {
                float2 i = floor(uv);
                float2 f = frac(uv);
                f = f * f * (3.0 - 2.0 * f);

                uv = abs(frac(uv) - 0.5);
                float2 c0 = i + float2(0.0, 0.0);
                float2 c1 = i + float2(1.0, 0.0);
                float2 c2 = i + float2(0.0, 1.0);
                float2 c3 = i + float2(1.0, 1.0);
                float r0 = Unity_SimpleNoise_RandomValue_float(c0);
                float r1 = Unity_SimpleNoise_RandomValue_float(c1);
                float r2 = Unity_SimpleNoise_RandomValue_float(c2);
                float r3 = Unity_SimpleNoise_RandomValue_float(c3);

                float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
                float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
                float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
                return t;
            }
             //TODO: move into separate hlsl file, generated by shadergraph
            void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out) {
                float t = 0.0;

                float freq = pow(2.0, float(0));
                float amp = pow(0.5, float(3-0));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(1));
                amp = pow(0.5, float(3-1));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(2));
                amp = pow(0.5, float(3-2));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                Out = t;
            }

            StructuredBuffer<float4x4> trsBuffer;
            sampler2D _MainTex;
            sampler2D _RenderTex;
            float4 _MainTex_ST;
            float4 _RenderTex_ST;
            float4 _PrimaryCol, _SecondaryCol, _AOColor, _TipColor;
            float _Scale;
            float4 _LightDir;
            float _MeshDeformationLimit;
            float4 _WindSpeed;
            float _WindStrength;
            float _WindNoiseScale;

            v2f vert (appdata v, uint instanceID : SV_InstanceID)
            {

                 v2f o;

                //applying transformation matrix
                float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(v.vertex.xyz, 1));

                //move world UVs by time
                float4 worldPos = float4(positionWorldSpace, 1);
                float2 worldUV = worldPos.xz + _WindSpeed * _Time.y;

                //creating noise from world UVs
                float noise = 0;
                Unity_SimpleNoise_float(worldUV, _WindNoiseScale, noise);
                noise = pow(noise, 2);

                //to keep bottom part of mesh at its position
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float smoothDeformation = smoothstep(0, _MeshDeformationLimit, o.uv.y);
                float distortion = smoothDeformation * noise;

                //apply distortion
                positionWorldSpace.x += distortion * _WindStrength;
                o.vertex = mul(UNITY_MATRIX_VP, float4(positionWorldSpace, 1));

                UNITY_TRANSFER_FOG(o,o.vertex);


                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
             
                float4 col = lerp(_PrimaryCol, _SecondaryCol, i.uv.y);

                //from https://github.com/GarrettGunnell/Grass/blob/main/Assets/Shaders/ModelGrass.shader
                float light = clamp(dot(_LightDir, normalize(float3(0, 1, 0))), 0 , 1);
                float4 ao = lerp(_AOColor, 1.0f, i.uv.y);
                float4 tip = lerp(0.0f, _TipColor, i.uv.y * i.uv.y * (1.0f + _Scale));
                float4 grassColor = (col + tip) * light * ao;


                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return grassColor;
            }
            ENDCG
        }
      
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
            ZWrite On
            ColorMask 0
            LOD 100
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            #pragma target 4.5
            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNoise_RandomValue_float (float2 uv)
            {
                  return frac(sin(dot(uv, float2(12.9898, 78.233)))*43758.5453);
            }
          
             //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNnoise_Interpolate_float (float a, float b, float t)
            {
                 return (1.0-t)*a + (t*b);
            }

             //TODO: move into separate hlsl file, generated by shadergraph
            inline float Unity_SimpleNoise_ValueNoise_float (float2 uv)
            {
                float2 i = floor(uv);
                float2 f = frac(uv);
                f = f * f * (3.0 - 2.0 * f);

                uv = abs(frac(uv) - 0.5);
                float2 c0 = i + float2(0.0, 0.0);
                float2 c1 = i + float2(1.0, 0.0);
                float2 c2 = i + float2(0.0, 1.0);
                float2 c3 = i + float2(1.0, 1.0);
                float r0 = Unity_SimpleNoise_RandomValue_float(c0);
                float r1 = Unity_SimpleNoise_RandomValue_float(c1);
                float r2 = Unity_SimpleNoise_RandomValue_float(c2);
                float r3 = Unity_SimpleNoise_RandomValue_float(c3);

                float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
                float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
                float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
                return t;
            }
             //TODO: move into separate hlsl file, generated by shadergraph
            void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out) {
                float t = 0.0;

                float freq = pow(2.0, float(0));
                float amp = pow(0.5, float(3-0));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(1));
                amp = pow(0.5, float(3-1));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                freq = pow(2.0, float(2));
                amp = pow(0.5, float(3-2));
                t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

                Out = t;
            }

            StructuredBuffer<float4x4> trsBuffer;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MeshDeformationLimit;
            float4 _WindSpeed;
            float _WindStrength;
            float _WindNoiseScale;

            v2f vert (appdata v, uint instanceID : SV_InstanceID)
            {
                v2f o;
         
                //applying transformation matrix
                float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(v.vertex.xyz, 1));

                //move world UVs by time
                float4 worldPos = float4(positionWorldSpace, 1);
                float2 worldUV = worldPos.xz + _WindSpeed * _Time.y;

                //creating noise from world UVs
                float noise = 0;
                Unity_SimpleNoise_float(worldUV, _WindNoiseScale, noise);
                noise = pow(noise, 2);

                //to keep bottom part of mesh at its position
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float smoothDeformation = smoothstep(0, _MeshDeformationLimit, o.uv.y);
                float distortion = smoothDeformation * noise;

                //apply distortion
                positionWorldSpace.x += distortion * _WindStrength;
                o.vertex = mul(UNITY_MATRIX_VP, float4(positionWorldSpace, 1));

                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

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

    Fallback "VertexLix"
}

1 Like

Small update:
I’ve researched again on this topic and found out I need the shadowCoords for the shadowMap.

By changing CGPROGRAM to HLSLPROGRAM, I can include the following files:

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"

This let’s me now compute the shadowcoord like this in the vertex shader:

o.shadowCoord = GetShadowCoord(vertexInput);```

The fragment shader then can retrieve the shadow data with:
```Light mainLight = GetMainLight(i.shadowCoord);```
And use ``` mainLight.distanceAttenuation``` and ```mainLight.shadowAttenuation```

BUT this does not work. Distance and shadow atten are both 0.
I've seen other approaches for the to get the shadowCoord with:
```o.shadowCoord = ComputeScreenPos(v.vertex)```
But that would only work for screen space shadows(??).

Anyways, when I use the ```ComputeScreenPos``` approach, I can at least see something changing on the material.. but it looks like the shadow coords are wrong. 

I'm lost.

Here's the shader pass I'm currently using:

[spoiler]

```csharp
Pass
        {

            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForwardOnly" }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            #pragma target 4.5

            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma shader_feature _ALPHATEST_ON

            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
            #include "noise.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 shadowCoord: TEXCOORD3;
                float3 normalWS : TEXCOORD1;
            };

            StructuredBuffer<float4x4> trsBuffer;
            sampler2D _MainTex;
            sampler2D _RenderTex;
            float4 _MainTex_ST;
            float4 _RenderTex_ST;
            float4 _PrimaryCol, _SecondaryCol, _AOColor, _TipColor;
            float _Scale;
            float4 _LightDir;
            float _MeshDeformationLimit;
            float4 _WindSpeed;
            float _WindStrength;
            float _WindNoiseScale;

            v2f vert (appdata v, uint instanceID : SV_InstanceID)
            {
                v2f o;

                //applying transformation matrix
                float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(v.vertex.xyz, 1));

                //move world UVs by time
                float4 worldPos = float4(positionWorldSpace, 1);
                float2 worldUV = worldPos.xz + _WindSpeed * _Time.y;

                //creating noise from world UVs
                float noise = 0;
                Unity_SimpleNoise_float(worldUV, _WindNoiseScale, noise);
                noise = pow(noise, 2);

                //to keep bottom part of mesh at its position
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float smoothDeformation = smoothstep(0, _MeshDeformationLimit, o.uv.y);
                float distortion = smoothDeformation * noise;

                //apply distortion
                positionWorldSpace.x += distortion * _WindStrength;
                o.vertex = mul(UNITY_MATRIX_VP, float4(positionWorldSpace, 1));


                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
                o.shadowCoord = GetShadowCoord(vertexInput);

                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.normalWS = normal.normalWS;
                o.shadowCoord = ComputeScreenPos(v.vertex);
               
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                float4 col = lerp(_PrimaryCol, _SecondaryCol, i.uv.y);

                //from https://github.com/GarrettGunnell/Grass/blob/main/Assets/Shaders/ModelGrass.shader
                float light = clamp(dot(_LightDir, normalize(float3(0, 1, 0))), 0 , 1);
                float4 ao = lerp(_AOColor, 1.0f, i.uv.y);
                float4 tip = lerp(0.0f, _TipColor, i.uv.y * i.uv.y * (1.0f + _Scale));
                float4 grassColor = (col + tip) * light * ao;

                 Light mainLight = GetMainLight(i.shadowCoord);
                 float strength = dot(mainLight.direction, i.normalWS);
                 float4 lightColor = float4(mainLight.color, 1)*(mainLight.distanceAttenuation * mainLight.shadowAttenuation);
               // half receiveshadow = MainLightRealtimeShadow(i.shadowCoord);


                return grassColor * lightColor * strength;
            }
            ENDHLSL
        }

[/spoiler]

Update: I got it working!
In my previous answer, I’ve had calculated the shadow coords wrong. So I digged trough Unity’s shaders again and found this line which gets called in the vertex shader:
inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS);
Then in the fragment shader I can simply do:
Light mainLight = GetMainLight(i.shadowCoord);
To access the mainLight.distanceAttenuation and mainLight.shadowAttenuation

This is also works: half s = MainLightRealtimeShadow(i.shadowCoord); but I don’t know what’s the difference because it gives me the same results.

Also weird thing I noticed is that it still works without an extra depth pass.

Anyways here is my full shader code for my gpu instanced grass that can cast and recieve shadows:
(still in the stage from my experiments, so not really refactored)

Shader "Unlit/GrassBladeIndirect"
{
    Properties
    {
        _MainTex ("Main Tex", 2D) = "white" {}
        _RenderTex ("Render Tex", 2D) = "white" {}
        _PrimaryCol ("Primary Color", Color) = (1, 1, 1)
        _SecondaryCol ("Secondary Color", Color) = (1, 0, 1)
        _AOColor ("AO Color", Color) = (1, 0, 1)
        _TipColor ("Tip Color", Color) = (0, 0, 1)
        _Scale ("Scale", Range(0.0, 2.0)) = 0.0
        _MeshDeformationLimit ("Mesh Deformation Limit", Range(0.0, 5.0)) = 0.0
        _WindNoiseScale ("Wind Noise Scale", float) = 0.0
        _WindStrength ("Wind Strength", float) = 1.0
        _WindSpeed ("Wind Speed", Vector) = (0, 0, 0, 0)
    }
    SubShader
    {
        LOD 100
        Cull Off
        ZWrite On
        Name "Grass"
        Tags {
            "RenderType"="Opaque"
            "RenderPipeline"="UniversalPipeline"
            "LightMode"="UniversalForwardOnly"
            "Queue"="Geometry"
        }

        Pass
        {

            Name "ForwardLit"
            Tags { "LightMode" = "UniversalForwardOnly" }

            HLSLPROGRAM

            #pragma vertex vert
            #pragma fragment frag

            #pragma prefer_hlslcc gles
            #pragma exclude_renderers d3d11_9x

            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            #pragma target 4.5

            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
            #pragma multi_compile _ _SHADOWS_SOFT
            #pragma shader_feature _ALPHATEST_ON

            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
            #pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE
            #include "noise.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal: NORMAL;
            };

            struct VertexOutput
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 shadowCoord: TEXCOORD2;
                float4 interp8 : INTERP8;
            };

            StructuredBuffer<float4x4> trsBuffer;
            sampler2D _MainTex;
            sampler2D _RenderTex;
            float4 _MainTex_ST;
            float4 _RenderTex_ST;
            float4 _PrimaryCol, _SecondaryCol, _AOColor, _TipColor;
            float _Scale;
            float4 _LightDir;
            float _MeshDeformationLimit;
            float4 _WindSpeed;
            float _WindStrength;
            float _WindNoiseScale;

            VertexOutput vert (VertexInput v, uint instanceID : SV_InstanceID)
            {
                VertexOutput o;


                //applying transformation matrix
                float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(v.vertex.xyz, 1));

                //move world UVs by time
                float4 worldPos = float4(positionWorldSpace, 1);
                float2 worldUV = worldPos.xz + _WindSpeed * _Time.y;

                //creating noise from world UVs
                float noise = 0;
                Unity_SimpleNoise_float(worldUV, _WindNoiseScale, noise);
                noise = pow(noise, 2);

                //to keep bottom part of mesh at its position
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float smoothDeformation = smoothstep(0, _MeshDeformationLimit, o.uv.y);
                float distortion = smoothDeformation * noise;

                //apply distortion
                positionWorldSpace.x += distortion * _WindStrength;
                o.vertex = mul(UNITY_MATRIX_VP, float4(positionWorldSpace, 1));

                VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
                o.shadowCoord = TransformWorldToShadowCoord(positionWorldSpace);

                VertexNormalInputs normal = GetVertexNormalInputs(v.normal);
                o.normal = normalize(mul(v.normal, (float3x3)UNITY_MATRIX_I_M));
               
                return o;
            }

            float4 frag (VertexOutput i) : SV_Target
            {
   
                float4 col = lerp(_PrimaryCol, _SecondaryCol, i.uv.y);

                //from https://github.com/GarrettGunnell/Grass/blob/main/Assets/Shaders/ModelGrass.shader
                float light = clamp(dot(_LightDir, normalize(float3(0, 1, 0))), 0 , 1);
                float4 ao = lerp(_AOColor, 1.0f, i.uv.y);
                float4 tip = lerp(0.0f, _TipColor, i.uv.y * i.uv.y * (1.0f + _Scale));
                float4 grassColor = (col + tip) * light * ao;

                 Light mainLight = GetMainLight(i.shadowCoord);
                 float strength = dot(mainLight.direction, i.normal);
                 float4 lightColor = float4(mainLight.color, 1)*(mainLight.distanceAttenuation * mainLight.shadowAttenuation);
              
                  half receiveshadow = MainLightRealtimeShadow(i.shadowCoord);

                return grassColor * receiveshadow;
            }
            ENDHLSL
        }

//Shadow caster pass     
        Pass {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
            ZWrite On
            ColorMask 0
            LOD 100
            Cull Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile_instancing
            #pragma target 4.5

            #include "UnityCG.cginc"
            #include "noise.hlsl"

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

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            StructuredBuffer<float4x4> trsBuffer;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MeshDeformationLimit;
            float4 _WindSpeed;
            float _WindStrength;
            float _WindNoiseScale;

            VertexOutput vert (VertexInput v, uint instanceID : SV_InstanceID)
            {
                VertexOutput o;
          
                //applying transformation matrix
                float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(v.vertex.xyz, 1));

                //move world UVs by time
                float4 worldPos = float4(positionWorldSpace, 1);
                float2 worldUV = worldPos.xz + _WindSpeed * _Time.y;

                //creating noise from world UVs
                float noise = 0;
                Unity_SimpleNoise_float(worldUV, _WindNoiseScale, noise);
                noise = pow(noise, 2);

                //to keep bottom part of mesh at its position
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                float smoothDeformation = smoothstep(0, _MeshDeformationLimit, o.uv.y);
                float distortion = smoothDeformation * noise;

                //apply distortion
                positionWorldSpace.x += distortion * _WindStrength;
                o.vertex = mul(UNITY_MATRIX_VP, float4(positionWorldSpace, 1));

                return o;
            }

           fixed4 frag (VertexOutput i) : SV_Target
           {
                SHADOW_CASTER_FRAGMENT(i);
           }
           ENDHLSL
       }
    }
}

Here’s noise.hlsl:

#ifndef NOISE
#define NOISE
// generated by shadergraph
inline float Unity_SimpleNoise_RandomValue_float(float2 uv)
{
    return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
}

// generated by shadergraph
inline float Unity_SimpleNnoise_Interpolate_float(float a, float b, float t)
{
    return (1.0 - t) * a + (t * b);
}

// generated by shadergraph
inline float Unity_SimpleNoise_ValueNoise_float(float2 uv)
{
    float2 i = floor(uv);
    float2 f = frac(uv);
    f = f * f * (3.0 - 2.0 * f);

    uv = abs(frac(uv) - 0.5);
    float2 c0 = i + float2(0.0, 0.0);
    float2 c1 = i + float2(1.0, 0.0);
    float2 c2 = i + float2(0.0, 1.0);
    float2 c3 = i + float2(1.0, 1.0);
    float r0 = Unity_SimpleNoise_RandomValue_float(c0);
    float r1 = Unity_SimpleNoise_RandomValue_float(c1);
    float r2 = Unity_SimpleNoise_RandomValue_float(c2);
    float r3 = Unity_SimpleNoise_RandomValue_float(c3);

    float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
    float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
    float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
    return t;
}

// generated by shadergraph
void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out)
{
    float t = 0.0;

    float freq = pow(2.0, float(0));
    float amp = pow(0.5, float(3 - 0));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x * Scale / freq, UV.y * Scale / freq)) * amp;

    freq = pow(2.0, float(1));
    amp = pow(0.5, float(3 - 1));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x * Scale / freq, UV.y * Scale / freq)) * amp;

    freq = pow(2.0, float(2));
    amp = pow(0.5, float(3 - 2));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x * Scale / freq, UV.y * Scale / freq)) * amp;

    Out = t;
}
#endif

2 Likes

Could you share your shader?
I’m looking for an unlit shader that works with shadows but I’m struggling quite a bit. I’ve stumbled upon the GetShadowCoord as well but get all kinds of errors when trying to use this :frowning:

The code is in my last reply but here’s a slightly refactored version (used for grass instancing)

Thanks! And my bad, apparently I can’t read.

Hi Thundernerd, thanks for the code, I think it’s really useful, it’s exactly what I’m looking for.

However, when I try and implement it into my shader I get a shadow across the whole object, as though they are always in shade no matter the direction of my direct main light. Is this something you came across before?

What exactly are you trying to do? The shader code I’ve posted is used for GPU instancing. Can you share some code?

here’s my vert and frag functions where I’ve tried to implement your code:

Varyings vert(Attributes IN, uint instanceID : SV_InstanceID)
{
Varyings OUT;

UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

float3 positionWorldSpace = mul(trsBuffer[instanceID], float4(IN.position.xyz, 1));

// VARIOUS VERTEXT MOVINGS HERE... //

// Transform vertex position to clip space
OUT.position = TransformObjectToHClip(IN.position.xyz);
OUT.uv = IN.uv.xy;

//getting normal for lighting calc
VertexPositionInputs posnInputs = GetVertexPositionInputs(IN.position);
VertexNormalInputs normInputs = GetVertexNormalInputs(IN.normal);

OUT.normalWS = normInputs.normalWS; //normalize(mul(v.normal, (float3x3)UNITY_MATRIX_I_M));
OUT.positionWS = posnInputs.positionWS;

return OUT;
}

float4 frag(Varyings IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);

float uvDirection = IN.uv.y;

float4 colorSample = lerp(_PrimaryCol, _SecondaryCol, uvDirection);
float4 ao = lerp(_AOColor, 1.0f, uvDirection);
float4 tip = lerp(0.0f, _TipColor, uvDirection * uvDirection * (1.0f + _Scale));
float4 grassColor = (colorSample + tip) * ao;

Light mainLight = GetMainLight(TransformWorldToShadowCoord(IN.positionWS)); 
float lightStrength = clamp(dot(mainLight.direction, IN.normalWS), 1.0, 1.0);

if (_RecieveShadow > 0) {
float distanceAtten = mainLight.distanceAttenuation;
float shadowAtten = mainLight.shadowAttenuation + 0.3;
float4 lightColor = float4(mainLight.color, 1) * (distanceAtten * shadowAtten) * _ShadowColor;

lightColor = saturate(lightColor);
grassColor = grassColor * lightColor * lightStrength;
}
return grassColor;
}

I am confused by the _RecieveShadow variable, as I can’t see where in your code you ‘change’ it? is it a built in URP variable?

When this is 1, the shadows are visible on the object, however, when I change the _ShadowColor, the whole object color changes, and the shadows remain dark.

Let me know if you need any more info, thanks for your help.

_RecieveShadow is a custom variable can be set via C# side. It acts like a boolean to decide wether I want my object to recieve any shadows or not. I think I just forgot to use it in my old code.

Your objects may look like they are always in shade because the lighting calc results are only applied if that condition is true.
And the whole object color changes as well since the _ShadowColor is first multiplied with the lightColor and then multiplied with the grassColor. So it influences the whole color.

Thanks for your reply.

So how do I decide to only multiply my _ShadowColor by grassColor? Surely I only do this when the pixel is in shade?

What can I use to decide this?

If the pixel is in shade or not is decided via float lightStrength = clamp(dot(mainLight.direction, IN.normalWS), 1.0, 1.0); which calculates the brightness of the pixel by the normal vector and main light direction. Then these two values come in to play

float distanceAtten = mainLight.distanceAttenuation;
float shadowAtten = mainLight.shadowAttenuation + 0.3;

to apply colors from the shadow map on to the pixel.
So to just change the shadow color you’d have to add the lightColor to the grassColor instead of multiplying it. May looks weird though.