Shadows cast in the wrong way

Hello I’ve been having problems with my shader code; I’ve tried to implement shadows in my code and they work fine but they are cast in the wrong way.
Here you can see they are cast as if the Terrain below it was receiving the shadows but the flat blue surface should be receiving the shadows instead since it is opaque.

{
    Properties
    {   
        _MainTex ("Texture", 2D) = "white" {}
        [Enum(Off,0,Front,1,Back,2)] _Cull ("Cull", Int) = 0

        _DepthMultiplier("Depth Multiplier", range(0,4)) = 1.2
        _ShoreFadeStrength ("Shore Fade Strength", Range(0, 1)) = 0.777
        _Smoothness ("Smoothness", range(0,1)) = 0.777

        _RipleScale ("Riple Wave Scale", float) = 1
        _RipleSpeed ("Riple Wave Speed", float) = 1
        _RipleFrequency ("Riple Wave Frequency", float) = 0.1

        _WaterNormalMap ("Wave Normal Texture", 2D) = "bump"
        _WaterNoiseMap ("Wave Noise Texture", 2D) = "white"
        _WaterDisplacementMap ("Wave Displacement Texture", 2D) = "white"

        _SurfaceNoiseScroll("Surface Noise Scroll Amount", Vector) = (0.03, 0.03, 0, 0)
    }
    SubShader
    {
       
        Tags{
            "RenderType"="Opaque"
            "LightMode"="ForwardBase"
            "PassFlags"="OnlyDirectional"
        }
        Cull [_Cull]
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert alpha
            #pragma fragment frag alpha
            #pragma multi_compile_fwdbase

            #include "UnityLightingCommon.cginc"
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

            uniform float _ShadowIntensity;

            struct vertexInput
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float3 tangent : TANGENT;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                LIGHTING_COORDS(0,1)
                float3 normal : TEXCOORD2;
                float4 worldPosition : TEXCOORD3;
                float4 screenPosition : TEXCOORD4;
                float3 viewVector : TEXCOORD5;
            };
           
            sampler2D _CameraDepthTexture;
            sampler2D _WaterDisplacementMap;

            sampler2D _WaterNoiseMap;
            sampler2D _WaterNormalMap;

            float4 _ColorShallow;
            float4 _ColorDeep;

            float _DepthMultiplier;
            float _AlphaMultiplier;
            float _ShoreFadeStrength;
            float _FoamDistance;

            float _NoiseScale;
            float _NoiseStrength;
           
            float _DisturbanceScale;
            float _DisturbanceSpeed;
            float _DisturbanceFrequency;
           
            float3 _WindDirection;
            float3 _DirToSun;
            float _WaveOffset;

            /* float _RipleFrequency;
            float _RipleSpeed;
            float _RipleScale; */

            bool _IsPlane = false;


            float CalculateNoise(float speed, float scale, float3 base){

                float2 noiseUV = float2(base.x/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.x/40*(_DisturbanceSpeed*speed), base.z/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.z/40*_DisturbanceSpeed);
                return tex2Dlod(_WaterNoiseMap, float4(noiseUV.x, noiseUV.y,0,0)).r;
            }

            float CalculateNewPosition(float3 base){

                return _DisturbanceScale * sin(_WaveOffset*2*_DisturbanceSpeed + (base.x*_WindDirection.x + base.z*_WindDirection.z) * _DisturbanceFrequency);
            }

            float3x3 CalculateNewNormal(float4 pos, float3 normal, float3 tangent, float offset, float smoothness){
               
                float3 position = pos;
                position.xyz += normal * (offset + CalculateNewPosition(pos));

                float3 modifiedTangent = pos + tangent;
                modifiedTangent.xyz += normal * (offset*smoothness + CalculateNewPosition(tangent));

                float3 modifiedBitangent = pos + cross(normal, tangent);
                modifiedBitangent.xyz += normal * (offset*smoothness + CalculateNewPosition(modifiedBitangent));

                modifiedTangent -= position;
                modifiedBitangent -= position;

                return float3x3(position, modifiedTangent, modifiedBitangent);
            }

            float calculateSpecular(float3 normal, float3 viewDir, float specularIntensity, float specularPower) {

                float specularAngle = acos(dot(normalize(_DirToSun - viewDir), normal));
                float specularExponent = specularAngle / specularIntensity;
                float specularHighlight = exp(-specularExponent * specularExponent)*specularPower;
                return specularHighlight;
            }

            v2f vert (vertexInput v)
            {
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
               
                float surfaceNoiseSample = CalculateNoise(0.5, 0.3, v.vertex);
                float offset = float(surfaceNoiseSample-0.5)*_NoiseStrength;
               
                float3x3 modifiedNormal = CalculateNewNormal(v.vertex, v.normal, v.tangent, offset, 2.5);

                float3 position = modifiedNormal._11_12_13;
                float3 modifiedTangent = modifiedNormal._21_22_23;
                float3 modifiedBitangent = modifiedNormal._31_32_33;

                o.pos = UnityObjectToClipPos(position);
                o.normal = normalize(mul(normalize(cross(modifiedTangent, modifiedBitangent)), (float3x3)unity_WorldToObject));

                o.worldPosition = mul(unity_ObjectToWorld, o.pos);
                o.screenPosition = ComputeScreenPos(o.pos);
                
                float3 viewVector = mul(unity_CameraInvProjection, float4((o.screenPosition.xy/o.screenPosition.w) * 2 - 1, 0, -1));
                o.viewVector = mul(unity_CameraToWorld, float4(viewVector,0));

                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;
            }


            float4 frag (v2f i) : SV_Target
            {

                float depthLinear = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition)).r;
                float terrainDepth = LinearEyeDepth(depthLinear);
                float waterDepth = i.screenPosition.w;
                float waterThickness = terrainDepth - waterDepth;

                float shoreFade = 1 - exp(-waterThickness * _ShoreFadeStrength);
                float fresnel = 1 - (saturate(dot(-_DirToSun, i.normal))/5 + (.5-_AlphaMultiplier));
                float waterAlpha = shoreFade * fresnel;

                float diffuseTerm = clamp(dot(_DirToSun, i.normal),0,1);
                float diffuseLight = (diffuseTerm+0.5)/2;


                float2 noiseUV = float2(i.worldPosition.x/3 + _WaveOffset*2*-_WindDirection.x/6, i.worldPosition.z/3 + _WaveOffset*2*-_WindDirection.z/6);
                float surfaceNoiseSample = tex2Dlod(_WaterNoiseMap, float4(noiseUV.x, noiseUV.y,0,0)).r;
               
                float surfaceFoam = waterThickness + tex2Dlod(_WaterNoiseMap, float4(i.worldPosition.x/100, i.worldPosition.y/100,0,0)).r/10 < 1 ? 1 : 0;
                surfaceFoam = saturate(surfaceFoam/(waterThickness*10));


                float3 waterColor = lerp(_ColorShallow * _LightColor0.xyz, _ColorDeep * _LightColor0.xyz, 1 - exp(-waterThickness * _DepthMultiplier));
                float specular = calculateSpecular(i.normal, normalize(i.viewVector), 0.3, 0.4);
                specular += calculateSpecular(i.normal, normalize(i.viewVector), 0.8, 0.15);

                float attenuation = LIGHT_ATTENUATION(i);
                surfaceFoam *= saturate(attenuation + 0.2);
                specular *= attenuation;

                return float4((waterColor + specular + surfaceFoam) * (diffuseLight * _LightColor0.xyz+.8 ) * saturate(attenuation + 0.7), waterAlpha);
            }
            ENDCG
        }
    }
}

I’m very confused as to how the Shadow pipeline works so if my mistake is obvious i’m sorry.

Thank you for any help.

Hi!
Try using UNITY_LIGHT_ATTENUATION and friends instead of LIGHT_ATTENUATION.

Hello and thank you for your answer!

I tried your suggestion but all it did was limit the distance from which the shadow can be seen (as seen in attached picture).
I’ve tried updating the macros to their newer UNITY_ versions but it changed nothing.

This is a transparent surface using the opaque queue without a shadow caster pass.

Unity’s built in renderer doesn’t support this kind of setup normally, because Unity casts shadows into the camera depth texture, not the geometry surface.

You can add a custom shadow caster pass, which is what is used to generate the camera depth texture, so the shadows show up on the surface of the water, but then you can’t do the depth fading / water edge you’re doing since the depth texture you’re sampling will just be seeing “itself”. This is an intentional limitation of the built in renderer that does not have an easy solution.

1 Like

Thank you very much. That explains my issue very well!

I don’t really care for the fade and the depth gradient I just want the vertex displacement and shadows to work so it’s not really a problem removing them

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

{
    Properties
    {   
        _MainTex ("Texture", 2D) = "white" {}
        [Enum(Off,0,Front,1,Back,2)] _Cull ("Cull", Int) = 0

        _Smoothness ("Smoothness", range(0,1)) = 0.777

        _RipleScale ("Riple Wave Scale", float) = 1
        _RipleSpeed ("Riple Wave Speed", float) = 1
        _RipleFrequency ("Riple Wave Frequency", float) = 0.1

        _WaterNormalMap ("Wave Normal Texture", 2D) = "bump"
        _WaterNoiseMap ("Wave Noise Texture", 2D) = "white"
        _WaterDisplacementMap ("Wave Displacement Texture", 2D) = "white"

        _SurfaceNoiseScroll("Surface Noise Scroll Amount", Vector) = (0.03, 0.03, 0, 0)
    }
    SubShader
    {
       
        Tags{
            "RenderType"="TransparentCutout"
            "LightMode"="ForwardBase"
            "PassFlags"="OnlyDirectional"
        }
        Cull [_Cull]
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert alpha
            #pragma fragment frag alpha
            #pragma multi_compile_fwdbase

            #include "UnityLightingCommon.cginc"
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"

            uniform float _ShadowIntensity;

            struct vertexInput
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float3 tangent : TANGENT;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                UNITY_LIGHTING_COORDS(0,1)
                float3 normal : TEXCOORD2;
                float4 worldPosition : TEXCOORD3;
                float4 screenPosition : TEXCOORD4;
                float3 viewVector : TEXCOORD5;
            };
           
            sampler2D _CameraDepthTexture;
            sampler2D _WaterDisplacementMap;

            uniform sampler2D _WaterNoiseMap;
            uniform sampler2D _WaterNormalMap;

            float4 _ColorShallow;
            float4 _ColorDeep;

            uniform float _NoiseScale;
            uniform float _NoiseStrength;
           
            uniform float _DisturbanceScale;
            uniform float _DisturbanceSpeed;
            uniform float _DisturbanceFrequency;
           
            uniform float3 _WindDirection;
            uniform float _WaveOffset;
            float3 _DirToSun;

            /* float _RipleFrequency;
            float _RipleSpeed;
            float _RipleScale; */

            bool _IsPlane = false;


            float CalculateNoise(float speed, float scale, float3 base){

                float2 noiseUV = float2(base.x/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.x/40*(_DisturbanceSpeed*speed), base.z/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.z/40*_DisturbanceSpeed);
                return tex2Dlod(_WaterNoiseMap, float4(noiseUV.x, noiseUV.y,0,0)).r;
            }

            float CalculateNewPosition(float3 base){

                return _DisturbanceScale * sin(_WaveOffset*2*_DisturbanceSpeed + (base.x*_WindDirection.x + base.z*_WindDirection.z) * _DisturbanceFrequency);
            }

            float3x3 CalculateNewNormal(float4 pos, float3 normal, float3 tangent, float offset, float smoothness){
               
                float3 position = pos;
                position.xyz += normal * (offset + CalculateNewPosition(pos));

                float3 modifiedTangent = pos + tangent;
                modifiedTangent.xyz += normal * (offset*smoothness + CalculateNewPosition(tangent));

                float3 modifiedBitangent = pos + cross(normal, tangent);
                modifiedBitangent.xyz += normal * (offset*smoothness + CalculateNewPosition(modifiedBitangent));

                modifiedTangent -= position;
                modifiedBitangent -= position;

                return float3x3(position, modifiedTangent, modifiedBitangent);
            }

            float calculateSpecular(float3 normal, float3 viewDir, float specularIntensity, float specularPower) {

                float specularAngle = acos(dot(normalize(_DirToSun - viewDir), normal));
                float specularExponent = specularAngle / specularIntensity;
                float specularHighlight = exp(-specularExponent * specularExponent)*specularPower;
                return specularHighlight;
            }

            v2f vert (vertexInput v)
            {
                v2f o;
                UNITY_INITIALIZE_OUTPUT(v2f, o);
               
                float surfaceNoiseSample = CalculateNoise(0.5, 0.3, v.vertex);
                float offset = float(surfaceNoiseSample-0.5)*_NoiseStrength;
               
                float3x3 modifiedNormal = CalculateNewNormal(v.vertex, v.normal, v.tangent, offset, 2.5);

                float3 position = modifiedNormal._11_12_13;
                float3 modifiedTangent = modifiedNormal._21_22_23;
                float3 modifiedBitangent = modifiedNormal._31_32_33;

                o.pos = UnityObjectToClipPos(position);
                o.normal = normalize(mul(normalize(cross(modifiedTangent, modifiedBitangent)), (float3x3)unity_WorldToObject));

                o.worldPosition = mul(unity_ObjectToWorld, o.pos);
                o.screenPosition = ComputeScreenPos(o.pos);
                
                float3 viewVector = mul(unity_CameraInvProjection, float4((o.screenPosition.xy/o.screenPosition.w) * 2 - 1, 0, -1));
                o.viewVector = mul(unity_CameraToWorld, float4(viewVector,0));


                TRANSFER_VERTEX_TO_FRAGMENT(o);
                return o;
            }


            float4 frag (v2f i) : SV_Target
            {

                /* float depthLinear = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition)).r;
                float terrainDepth = LinearEyeDepth(depthLinear);
                float waterDepth = i.screenPosition.w;
                float waterThickness = terrainDepth - waterDepth;*/

                //float shoreFade = 1 - exp(-waterThickness * _ShoreFadeStrength);
                float fresnel = 1 - (saturate(dot(-_DirToSun, i.normal))/5 + (.5-_AlphaMultiplier));
                //float waterAlpha = shoreFade * fresnel;

                float diffuseTerm = clamp(dot(_DirToSun, i.normal),0,1);
                float diffuseLight = (diffuseTerm+0.5)/2;


                float2 noiseUV = float2(i.worldPosition.x/3 + _WaveOffset*2*-_WindDirection.x/6, i.worldPosition.z/3 + _WaveOffset*2*-_WindDirection.z/6);
                float surfaceNoiseSample = tex2Dlod(_WaterNoiseMap, float4(noiseUV.x, noiseUV.y,0,0)).r;
               
                /* float surfaceFoam = waterThickness + tex2Dlod(_WaterNoiseMap, float4(i.worldPosition.x/100, i.worldPosition.y/100,0,0)).r/10 < 1 ? 1 : 0;
                surfaceFoam = saturate(surfaceFoam/(waterThickness*10)); */


                float3 waterColor = lerp(_ColorShallow * _LightColor0.xyz, _ColorDeep * _LightColor0.xyz, 1);
                float specular = calculateSpecular(i.normal, normalize(i.viewVector), 0.3, 0.4);
                specular += calculateSpecular(i.normal, normalize(i.viewVector), 0.8, 0.15);

                //UNITY_LIGHT_ATTENUATION(attenuation, i, i.worldPosition.xyz);
                float attenuation = LIGHT_ATTENUATION(i);
                //attenuation = 1;
                //float attenuation = SHADOW_ATTENUATION(i);
                surfaceFoam *= saturate(attenuation + 0.2);
                specular *= attenuation;

                fresnel = saturate(fresnel+specular) - saturate(attenuation-0.8);

                //return float4(attenuation,0,0,1);
                return float4((waterColor + specular + surfaceFoam) * (diffuseLight * _LightColor0.xyz+.8 ) * saturate(attenuation + 0.85), fresnel);
            }
            ENDCG
        }

        Pass
        {
            Name "ShadowCaster"
            Tags {"LightMode"="ShadowCaster"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
            struct v2f {
                V2F_SHADOW_CASTER;
            };

            sampler2D _WaterNoiseMap;
            sampler2D _WaterNormalMap;

            uniform float _NoiseScale;
            uniform float _NoiseStrength;
           
            uniform float _DisturbanceScale;
            uniform float _DisturbanceSpeed;
            uniform float _DisturbanceFrequency;
           
            uniform float3 _WindDirection;
            uniform float _WaveOffset;

            float CalculateNoise(float speed, float scale, float3 base){

                float2 noiseUV = float2(base.x/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.x/40*(_DisturbanceSpeed*speed), base.z/150*(_NoiseScale*scale) + _WaveOffset*2*-_WindDirection.z/40*_DisturbanceSpeed);
                return tex2Dlod(_WaterNoiseMap, float4(noiseUV.x, noiseUV.y,0,0)).r;
            }

            float CalculateNewPosition(float3 base){

                return _DisturbanceScale * sin(_WaveOffset*2*_DisturbanceSpeed + (base.x*_WindDirection.x + base.z*_WindDirection.z) * _DisturbanceFrequency);
            }

            v2f vert(appdata_base v)
            {
                v2f o;
                TRANSFER_SHADOW_CASTER(o)
               
                float surfaceNoiseSample = CalculateNoise(0.5, 0.3, v.vertex);
                float offset = float(surfaceNoiseSample-0.5)*_NoiseStrength;

                float3 position = v.vertex;
                position.xyz += v.normal * (offset + CalculateNewPosition(position));

                o.pos = UnityObjectToClipPos(position);

                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }
            float4 frag(v2f i) : SV_Target
            {
                SHADOW_CASTER_FRAGMENT(i)
            }
            ENDCG
        }
       
    }
}

.
So that’s what I tried to do, I added a ShadowCaster Pass and tried to change the position of the cast to fit the displacement made in the first vertex shader but it “clips” some fragments under a certain height treshhold and returns an “empty pixel” instead of the color I’m giving it.

7482209--920267--shader shadow.png