float rtao(vec3 normal, vec3 fragpos)
{
    const int samples = 4;
    float occlusion = 0.0;

    mat3 modelView3 = mat3(gbufferModelView);
    vec3 clipPosition = toClipSpace3(fragpos);
    vec2 bn4 = blueNoise4(gl_FragCoord.xy).rg;
    vec2 invTexelSize = 1.0 / texelSize;
    int seed = frameCounter * samples;

    float sqrt3Near = 1.732051 * near;
    float sqrt3Far = 1.732051 * far;

    for (int i = 0; i < samples; i++)
    {
        vec2 ij = fract(vec2(0.75487765, 0.56984026) * float(seed + i) + bn4);
        float angle = ij.x * 6.2831853;
        float s = sin(angle), c = cos(angle);
        ij.y = ij.y * 2.0 - 1.0;

        vec3 L = normalize(normal + vec3(s * sqrt(1.0 - ij.y * ij.y), c * sqrt(1.0 - ij.y * ij.y), ij.y));
        vec3 dir = modelView3 * L;

        float rayLength = ((fragpos.z + dir.z * sqrt3Far) > -sqrt3Near) ? (-sqrt3Near - fragpos.z) / dir.z : sqrt3Far;
        vec3 end = toClipSpace3(fragpos + dir * rayLength);
        vec3 direction = end - clipPosition;

        float len = max(abs(direction.x) * invTexelSize.x, abs(direction.y) * invTexelSize.y);
        vec3 spos = clipPosition + (direction / len) * (64.0 * (bn4.x + bn4.y) + 1.0);
        spos.xy += offsets[framemod8] * texelSize * 0.5;

        float sp = ld(texelFetch(depthtex0, ivec2(spos.xy * invTexelSize), 0).r);
        occlusion += 1.0 - float((sp < ld(spos.z)) && (abs(sp - ld(spos.z)) / ld(spos.z) < 0.1));
    }

    return occlusion * 0.25;
}
