vec4 screenRT(vec3 viewPos, vec3 viewNormal, float dither, bool water) {
    const uint steps = 15;
    const uint refine = 5;

    vec3 viewDir        = normalize(viewPos);

    vec3 reflectDir     = reflect(viewDir, viewNormal);

    if (dot(reflectDir, viewNormal) < 0.0) reflectDir = -reflectDir;

    float nDotV     = saturate(1.0 - dot(viewDir, viewNormal));

    vec3 startPos   = viewPos;
    vec3 startScreenPos  = viewToScreenSpace(startPos);
    vec3 rStep      = reflectDir * mix(1.0, 0.15, nDotV);
    vec3 rPos       = startPos + rStep * dither;
        if (!water) rPos += rStep * 0.1;

    bool hit    = false;
    bool hitBehind = false;
    float hitErrorScale = 1.0;

    vec3 hitPos = vec3(0.0);

    const float refineMult  = pow(0.5, float(refine));

    vec3 previousStep   = rStep;

    for (uint i = 0; i < steps; ++i, rPos += rStep) {
        rPos   += dither * (rStep - previousStep);
        vec3 screenPos  = viewToScreenSpace(rPos);

        previousStep    = rStep;
        if (saturate(screenPos.xy) != screenPos.xy) return vec4(0.0);

        float depth     = texelFetch(depthtex1, ivec2(screenPos.xy * viewSize), 0).x;
        vec3 sampleViewPos = screenToViewSpace(vec3(screenPos.xy, depth));

        float error     = distance(rPos, sampleViewPos);

        float slength   = length(rStep);

        float delta     = rPos.z - sampleViewPos.z;

        bool potentialHit = delta <= 0.0 || distance(startPos, sampleViewPos) > 0.5;

        //if (error < slength && (distance(startPos, sampleViewPos) > (slength / 10.0) || water)) {
        if (potentialHit && error < slength) {
            hitBehind = screenPos.z >= depth;
            hit = true;
            hitErrorScale = (hitBehind ? slength - error : error) * rcp(slength + 1e-10);
            hitPos = rPos;
            break;
        }

        if (error > (slength * 4.0) && slength < 128.0) rStep *= 2.0;
    }

    if (hit) {
        //bool refineHit  = false;

        vec3 refineStep = rStep * hitErrorScale * (nDotV * 0.8 + 0.1);
        vec3 refinePos  = rPos;
        if (hitBehind) refinePos -= rStep;
        //else refinePos += rStep * hitErrorScale * (nDotV * 0.8 + 0.1);

        vec3 refineHitPos = hitPos;
        float slength   = length(rStep);

        for (uint j = 0; j < refine; ++j, refinePos += refineStep) {
            vec3 screenPos  = viewToScreenSpace(refinePos);

            if (saturate(screenPos.xy) != screenPos.xy) return vec4(0.0);

            float depth     = texelFetch(depthtex1, ivec2(screenPos.xy * viewSize), 0).x;
            vec3 sampleViewPos = screenToViewSpace(vec3(screenPos.xy, depth));

            float error     = distance(rPos, sampleViewPos);

            if (screenPos.z > depth) refinePos -= refineStep;
            else refineHitPos = refinePos;

            float delta     = rPos.z - sampleViewPos.z;

            if (error < (slength * refineMult) && (delta <= 0.0 || screenPos.z <= depth)) {
                break;
            }
            refineStep *= 0.5;
        }

        vec3 stepDirection  = (refineHitPos - viewPos);

        vec3 screenPos  = viewToScreenSpace(refineHitPos);


        if (landMask(texture(depthtex1, screenPos.xy).x)) {
            
            //if (dot(normalize(stepDirection), viewNormal) > 0.8) return vec4(0.0);

            return vec4(texture(colortex0, screenPos.xy).rgb, 1.0);
        }
    }

    return vec4(0.0);
}

vec4 screenRT_Fast(vec3 viewPos, vec3 viewNormal, float dither, bool water) {
    const uint steps = 12;
    const uint refine = 3;

    vec3 viewDir        = normalize(viewPos);

    vec3 reflectDir     = reflect(viewDir, viewNormal);

    float nDotV     = saturate(1.0 - dot(viewDir, viewNormal));

    vec3 startPos   = viewPos;
    vec3 startScreenPos  = viewToScreenSpace(startPos);
    vec3 rStep      = reflectDir * mix(1.0, 0.15, nDotV);
    vec3 rPos       = startPos + rStep * dither;
        if (!water) rPos += rStep * 0.1;

    bool hit    = false;
    bool hitBehind = false;
    float hitErrorScale = 1.0;

    vec3 hitPos = vec3(0.0);

    const float refineMult  = pow(0.5, float(refine));

    vec3 previousStep   = rStep;

    for (uint i = 0; i < steps; ++i, rPos += rStep) {
        rPos   += dither * (rStep - previousStep);
        vec3 screenPos  = viewToScreenSpace(rPos);

        previousStep    = rStep;
        if (saturate(screenPos.xy) != screenPos.xy) return vec4(0.0);

        float depth     = texelFetch(depthtex1, ivec2(screenPos.xy * viewSize), 0).x;
        vec3 sampleViewPos = screenToViewSpace(vec3(screenPos.xy, depth));

        float error     = distance(rPos, sampleViewPos);

        float slength   = length(rStep);

        float delta     = rPos.z - sampleViewPos.z;

        bool potentialHit = delta <= 0.0 || distance(startPos, sampleViewPos) > 0.5;

        //if (error < slength && (distance(startPos, sampleViewPos) > (slength / 10.0) || water)) {
        if (potentialHit && error < slength) {
            hitBehind = screenPos.z >= depth;
            hit = true;
            hitErrorScale = (hitBehind ? slength - error : error) * rcp(slength + 1e-10);
            hitPos = rPos;
            break;
        }

        if (error > (slength * 4.0) && slength < 128.0) rStep *= 2.0;
    }

    if (hit) {
        //bool refineHit  = false;

        vec3 refineStep = rStep * hitErrorScale * (nDotV * 0.8 + 0.1);
        vec3 refinePos  = rPos;
        if (hitBehind) refinePos -= rStep;
        //else refinePos += rStep * hitErrorScale * (nDotV * 0.8 + 0.1);

        vec3 refineHitPos = hitPos;
        float slength   = length(rStep);

        for (uint j = 0; j < refine; ++j, refinePos += refineStep) {
            vec3 screenPos  = viewToScreenSpace(refinePos);

            if (saturate(screenPos.xy) != screenPos.xy) return vec4(0.0);

            float depth     = texelFetch(depthtex1, ivec2(screenPos.xy * viewSize), 0).x;
            vec3 sampleViewPos = screenToViewSpace(vec3(screenPos.xy, depth));

            float error     = distance(rPos, sampleViewPos);

            if (screenPos.z > depth) refinePos -= refineStep;
            else refineHitPos = refinePos;

            float delta     = rPos.z - sampleViewPos.z;

            if (error < (slength * refineMult) && (delta <= 0.0 || screenPos.z <= depth)) {
                break;
            }
            refineStep *= 0.5;
        }

        vec3 stepDirection  = (refineHitPos - viewPos);

        vec3 screenPos  = viewToScreenSpace(refineHitPos);


        if (landMask(texture(depthtex1, screenPos.xy).x)) {
            
            //if (dot(normalize(stepDirection), viewNormal) > 0.8) return vec4(0.0);

            return vec4(texture(colortex0, screenPos.xy).rgb, 1.0);
        }
    }

    return vec4(0.0);
}
