[SRP] Write to depth buffer before water shader to reduce the apparent depth of the water


Consider this water shader. I takes the distance from the water surface to the depth buffer and tints the image from transparent, to clear teal, to dark teal. The further the darker/more opaque. (in the case of the camera being underwater I place a quad in front of the camera so that it looks exactly the same as looking from above the water)
The issue is that in the screenshoot I am looking from under water towards the sky, if I were to raycast from the camera, the ray would leave the water quite early, that is to say, there is very little water between the camera and the tunnel ceiling.
The issue is that my shader don’t take that into consideration.

My current solution that does not work is as follow:
Have a ‘water back’ mesh that uses a shader that writes it’s position to the depth buffer, so that when the water shader reads the depth buffer, it reads a lower depth value and thus does not darken the image as much as it does right now, the closer the camera being to the ‘water back’ mesh, the more transparent the water is.

The issue is that I can’t get it to work, I’ve tried quite a few variations of the following shader but none seem to work and I am not sure what I am doing wrong.

Shader "Custom/InvisibleMask" {
    Properties{
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry-1"} //actually rendering at 2999 that is 1 less than the water in the material
        LOD 100
        ColorMask 0
        Pass{
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            sampler2D _CameraDepthTexture;
            struct appdata{
                float4 vertex : POSITION;
            };
            struct v2f{
                float4 vertex : SV_POSITION;
                float4 screenPos : TEXCOORD0;
            };
            v2f vert (appdata v){
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.screenPos = ComputeScreenPos(o.vertex);
                return o;
            }
            float frag (v2f i) : SV_Depth{
                float4 screenPos = float4(i.screenPos.xyz, i.screenPos.w + 0.00000000001);
                float4 screenPosNorm = screenPos / screenPos.w;
                screenPosNorm.z = (UNITY_NEAR_CLIP_VALUE >= 0) ? screenPosNorm.z : screenPosNorm.z * 0.5 + 0.5;

                float screenDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenPosNorm.xy);
                return .92375;//this value either completely removes the water or the water is completely unaffected by it
                //returning neither screenPosNorm.z nor screenDepth gave the intended effect, it either did nothing or deleted the water
            }
            ENDCG
        }
    }
}

I got it half working.


However, it projects shadows projected onto it into the distance.

(water effect disabled to make easier to notice the bridge shadow)

The bridge shadow is being projected onto the background, despite the mesh triangles facing away from the shadow and the mesh renderer having everything turned off
9451634--1327043--upload_2023-11-5_15-37-54.png

I assume it’s somehow related to Tags{ “LightMode” = “ShadowCaster” } which seems to be the only one that lets me write to the depth buffer, but also makes it write to shadows or something.
Rendering the shader at queue 2500 makes it work + shadow bug, rendering at 2501 and over makes it not work at all but at least does not have the shadow bug.

This is the shader:

Shader "Custom/InvisibleMask" {
    Properties {}
    SubShader {
        Tags {           
            "RenderType"="Opaque"
        }
        Cull Back
        ColorMask 0
        Pass{
            Tags{ "LightMode" = "ShadowCaster" }
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

                struct appdata {
                    float4 vertex : POSITION;
                };

                struct v2f {
                    float4 vertex : SV_POSITION;
                };


                v2f vert(appdata v) {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);//float4(0,0,0,0);//
                    return o;
                }

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