

float RT(vec3 dir, vec3 position, float noise)
{

    float stepSize = 6;

    vec3 clipPosition = toClipSpace3(position);
    float rayLength = ((position.z + dir.z * sqrt(3.0) * far) > -sqrt(3.0) * near) ? (-sqrt(3.0) * near - position.z) / dir.z : sqrt(3.0) * far;

    vec3 end = toClipSpace3(position + dir * rayLength);
    vec3 direction = end - clipPosition;
    float len = max(abs(direction.x) / texelSize.x, abs(direction.y) / texelSize.y) / stepSize;

    vec3 stepv = direction / len;

    vec3 spos = clipPosition + stepv / stepSize * 64.0 * noise;
    spos.xy += offsets[framemod8] * texelSize * 0.5;

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

    float currZ = linZ(spos.z);

    if (sp < currZ)
    {
        float dist = abs(sp - currZ) / currZ;

        if (dist <= mix(0.02, 0.2, noise))
            return 0.0;
    }

    return 1.0;
}

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)
{
    // Apparently this is actually this simple.
    // http://www.amietia.com/lambertnotangent.html
    // (cosine lobe around vector = lambertian BRDF)
    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, noise_standard(gl_FragCoord.xy));
    }

    return pow((occlusion / float(samples)), 1.5);
}
