

float RT(vec3 dir, vec3 position, float noise)
{
    vec3 clipPosition = toClipSpace3(position);

    // Precompute constants for efficiency
    float sqrt3Near = sqrt(3.0) * near;
    float sqrt3Far = sqrt(3.0) * far;

    // Compute ray length with optimized conditional expression
    float rayLength = (position.z + dir.z * sqrt3Far > -sqrt3Near)
                          ? (-sqrt3Near - position.z) / dir.z
                          : sqrt3Far;

    // Compute the end position in clip space
    vec3 end = toClipSpace3(position + dir * rayLength);

    // Compute direction and length in a single step
    vec3 direction = end - clipPosition;
    float invTexelSizeX = 1.0 / texelSize.x;
    float invTexelSizeY = 1.0 / texelSize.y;
    float len = max(abs(direction.x) * invTexelSizeX, abs(direction.y) * invTexelSizeY);

    // Calculate step vector and scale with noise
    vec3 stepv = direction / len;
    vec3 scaledStep = stepv * (64.0 * noise + 1.0);

    // Update position with scaled step
    vec3 spos = clipPosition + scaledStep;

    // Offset with jittered sampling for anti-aliasing
    spos.xy += offsets[framemod8] * texelSize * 0.5;

    // Sample the depth texture and linearize depth
    float sp = linZ(texelFetch(depthtex0, ivec2(spos.xy / texelSize), 0).r);

    // Linearize current Z position
    float currZ = linZ(spos.z);

    // Compute the output value with simplified conditional
    float out1 = 1.0 - float((sp < currZ) && (abs(sp - currZ) / currZ < 0.1));

    return out1;
}

vec2 R2_samples(int n)
{
    vec2 alpha = vec2(0.75487765, 0.56984026);
    return fract(alpha * n);
}
vec3 GenerateUnitVector(vec2 xy)
{
    const float tau = radians(360.0);

    xy.x *= tau;
    xy.y = xy.y * 2.0 - 1.0;
    return vec3(vec2(cos(xy.x), sin(xy.x)) * sqrt(1.0 - xy.y * xy.y), xy.y);
}

vec3 GenerateCosineVector(vec3 vector, vec2 xy)
{

    return normalize(vector + GenerateUnitVector(xy));
}

float rtao(float skyLight, vec3 normal, vec3 fragpos)
{

    int samples = 4;
    float occlusion = 0.0;
    for (int i = 0; i < samples; i++)
    {
        int seed = frameCounter * int(samples) + i;
        vec2 ij = fract(R2_samples(seed) + blueNoise4(gl_FragCoord.xy).rg);
        vec3 L = GenerateCosineVector(normal, ij);

        occlusion += RT(mat3(gbufferModelView) * L, fragpos.xyz, blueNoise(gl_FragCoord.xy));
    }

    return occlusion / float(samples);
}
