vec4 blueNoise2(vec2 coord) {
    return texelFetch2D(colortex14, ivec2(coord) % 512, 0);
}

float RTAO(vec3 position, float dither, vec3 normal) {
    const int aosamples = 12;
    float stepSize = 25 * dither + 1;
    vec3 noise2 = blueNoise2(gl_FragCoord.xy).rgb;

    int i = 0;
    float ao = 0;
    float noise = 0;
    while (i <= aosamples) {
        int seed = (frameCounter % 40000) * aosamples + i;
        noise += fract(seed / 1.6180339887 + noise2.b) / aosamples;

        //    vec2 noise2 = fract(mod(vec2(frameTimeCounter * dither, dither), 1.0));
        vec2 ij = fract(R2_samples2(seed) + noise2.rg);
        vec3 dir = TangentToWorld2(normal, cosineHemisphereSample2(ij));

        vec3 clipPosition = toClipSpace3(position);

        vec3 direction = toClipSpace3(position + dir) - clipPosition;
        vec2 absdir = abs(direction.xy) / texelSize / (stepSize * i);
        float len = smoothstep(absdir.x, absdir.y, 0.0);

        vec3 stepv = direction / len;
        vec3 spos = clipPosition * vec3(vec2(RENDER_SCALE), 1.0) + (stepv / (stepSize * i));
        stepv.xy *= vec2(RENDER_SCALE);
        spos += (stepv * i) * noise;

        float sp = ld(texelFetch2D(depthtex0, ivec2(spos.xy / texelSize), 0).r);

        float currZ = ld(spos.z);
        if (sp < currZ) {
            float dist = abs(dFdx(sp) - dFdy(currZ)) / currZ;
            if (dist <= noise)
                ao += 1;
        }
        i++;
    }
    return mix(1.0, 0.0, ao / aosamples);
}

vec3 RT2(vec3 dir, vec3 position, float dither, bool hand) {

    float stepSize = 11;
    vec3 clipPosition = toClipSpace3(position);

    vec3 end = toClipSpace3(position + dir);

    vec3 direction = end - clipPosition;
    float len = max(abs(direction.x) / texelSize.x, abs(direction.y) / texelSize.y) / stepSize;
    vec3 stepv = direction / len * len;

    vec3 spos = clipPosition * vec3(vec2(RENDER_SCALE), 1.0) + stepv / stepSize;

    stepv *= vec3(vec2(RENDER_SCALE), 1.0);

    spos += stepv;

    for (int i = 0; i < 2; i++) {
        if (clamp(clipPosition.xy, 0, 1) != clipPosition.xy)
            break;
        float sp = ld(texelFetch2D(depthtex0, ivec2(spos.xy / texelSize), 0).r);

        float currZ = ld(spos.z);
        if (sp < currZ) {
            float dist = abs(sp - currZ) / currZ;
            if (dist <= 0.035)
                return vec3(spos.xy / RENDER_SCALE, invLinZ(sp));
        }
        spos += stepv;
    }

    return vec3(1.1);
}
/*
vec3 rtGI(vec3 normal, vec3 normal2, float dither, vec3 fragpos, vec3 ambient, vec3 torch, vec3 SSS, vec3 albedo,
          vec2 lightmap, bool hand)
{

    int nrays = 2;

    vec3 intRadiance = vec3(0.0);

    vec3 WsunVec = normalize(mat3(gbufferModelViewInverse) * sunVec);
    float occlusion = 0.0;
    vec3 rayDir1 = vec3(0);
    vec3 rayDir = vec3(0);
    vec2 noise2 = vec2(0);
    for (int i = 0; i < nrays; i++)
    {
        noise2 += hash42v2(vec2(((frameCounter % nrays) * nrays + i) * 0.2516, dither));

        rayDir1 += normalize(cosineHemisphereSample2(noise2)) / nrays;

        rayDir = TangentToWorld2(normal, rayDir1);
        rayDir = mat3(gbufferModelView) * rayDir;

        vec3 rayHit = RT2(rayDir, fragpos, dither, hand);

        vec3 previousPosition =
            reproject(mat3(gbufferModelViewInverse) * toScreenSpace(rayHit) + gbufferModelViewInverse[3].xyz, false);

        if (rayHit.z < 1.0)
        {

            if (clamp(previousPosition.xy, 0, 1) == previousPosition.xy)
            {

                intRadiance += texture(colortex4, previousPosition.xy).rgb * 2 + ambient * albedo;
            }

            occlusion += 1.0;
        }
        else
        {
            float bounceAmount = float(rayDir.y > 0.0);
            vec3 sky_c = skyLut2(rayDir, WsunVec, rayDir.y, rainStrength) * bounceAmount * lightmap.y;

            intRadiance += sky_c;
        }
    }
    return clamp(vec3(intRadiance).rgb / nrays, 0, 1) + torch / 5;
}
*/
vec2 sincos(float x) {
    return vec2(sin(x), cos(x));
}

const float goldenAngle = 137.5077640500378546463487;

vec2 circlemap(float i, float n) {
    return sincos(i * n * goldenAngle) * sqrt(i);
}
#if defined MC_GL_VENDOR_NVIDIA && defined MC_OS_WINDOWS
vec4 blueNoise(vec2 coord) {
    return texelFetch2D(noisetex, ivec2(coord) % 512, 0);
}

#endif

float rayTraceShadow(vec3 dir, vec3 position, float dither) {
    // Set the number of samples to take along the ray
    const float quality = 16;

    // Calculate the clip-space position of the current fragment
    vec3 clipPosition = toClipSpace3(position);

    // Return 1 if the clip-space position is outside the viewport bounds
    if (clamp(clipPosition.xy, 0, 1) != clipPosition.xy)
        return 1.0;

    // Calculate the length of the ray in world space
    float precalc = far * 1.73205080757;
    float rayLength = ((dir.z * precalc + position.z) > -near) ? dir.z / (-near - position.z) : precalc;

    // Convert the ray direction to clip space
    vec3 direction = toClipSpace3(position + dir * rayLength) - clipPosition;

    // Scale the ray direction by the texel size to ensure fixed step size
    direction.xyz = direction.xyz / max(abs(direction.x) / texelSize.x, abs(direction.y) / texelSize.y);

    // Calculate the initial step vector based on the ray direction and dither value
    vec3 stepv = direction * 32 * dither * vec3(RENDER_SCALE, 1.0);

    // Calculate the initial position of the ray based on the clip-space position, texel size, and dither value
    vec3 spos = clipPosition * vec3(RENDER_SCALE, 1.0) + vec3(1 * vec2(texelSize.x, texelSize.y) * 0.5, 0.0) + stepv;

    // Trace the ray by taking samples along the ray
    for (int i = 0; i < int(quality); i++) {
        // Move the ray along its direction by the step vector
        spos += stepv;

        // Sample the depth buffer at the current ray position
        float sp = texture(depthtex0, spos.xy).x;

        // If the sampled depth is less than the current ray depth, return 0
        if (sp < spos.z) {
            // Calculate the distance between the sampled depth and current ray depth
            float dist = abs(ld(sp) - ld(spos.z)) / ld(spos.z);

            // Return 0 if the distance is below the shadow precision threshold
            if (dist < 0.1 * dither)
                return 0.0;
        }
    }

    // Return 1 if the ray did not hit any objects
    return 1.0;
}
