// vec3 screenSpaceRayTrace(vec3 viewPos, vec3 dir, float nDotv, sampler2D tex, uint steps, float dither, out bool hit, out bool isValid) {
//     isValid = false;
//     hit = false;

//     vec4 rayPos = gbufferProjection * vec4(viewPos, 1.0);
//         rayPos.xyz /= rayPos.w;
//         rayPos.xyz = rayPos.xyz * 0.5 + 0.5; // screenspace

//     vec4 rayStep = gbufferProjection * vec4(dir * mix(1.0, 0.15, nDotv), 0.0);
//         rayStep.xyz /= rayStep.w;
//         rayStep.xyz = rayStep.xyz * 0.5 + 0.5;

//     float diff = 0.0;

//     for (uint i = 0; i < steps; i++) {
//         rayPos.xyz += rayStep.xyz;
//         vec2 screenPos = rayPos.xy*0.5 + 0.5;
//         if(clamp01(screenPos) != screenPos) {
//             isValid = false;
//             hit = false;
//             return getDepthPoint(rayPos.xy, rayPos.z);
//         }
//         diff = texture2D(tex, screenPos).r - abs(rayPos.z);
//         if(diff < 0 || true){// && diff > -0.06) {
//             isValid = true;
//             hit = true;
//             return getDepthPoint(rayPos.xy, rayPos.z);
//         } 
//     }

//     isValid = true;

//     return getDepthPoint(rayPos.xy, rayPos.z);
// }

vec3 screenSpaceRayTrace(vec3 viewPos, vec3 dir, float nDotv, sampler2D tex, uint steps, float dither, out bool hit, out bool isValid) {
    const float bias = 1.3;

    isValid = false;
    hit = false;

    float viewFactor = mix(0.5, 0.15, nDotv);
    vec3 rayStep = dir * viewFactor;
    float maxRayDist = viewFactor * pow(1.8, float(steps));
    vec3 rayPos = viewPos + rayStep * dither;

    float diff = 0.0;

    for (uint i = 0; i < steps; i++) {
        rayPos += rayStep;
        vec2 screenPos = viewToScreen(rayPos);
        if(clamp01(screenPos) != screenPos) {
            isValid = false;
            hit = false;
            return rayPos;
        }
        float depth = texture2D(tex, screenPos).r;
        diff = -linearizeDepth(depth, gbufferProjectionInverse) - abs(rayPos.z);
        if(diff < 0 && diff > -length(rayStep)*bias && depth < far){// && distance(rayPos, viewPos) < maxRayDist) {
            isValid = true;
            hit = true;
            return rayPos;
        } else if(diff < -length(rayStep)*bias) {
            isValid = false;
            hit = false;
            return rayPos;
        }
        rayStep *= 1.8;
    }

    isValid = true;

    return rayPos;
}