#version 120
varying vec2 uv;

uniform sampler2D sMRT1;
uniform sampler2D sMRT2;
uniform sampler2D sRand;
	
uniform vec4 cViewportSize; // (viewport_width, viewport_height, inverse_viewport_width, inverse_viewport_height)
uniform float cFov; // vertical field of view in radians
uniform float cSampleInScreenspace; // whether to sample in screen or world space
uniform float cSampleLengthScreenSpace; // The sample length in screen space [0, 1]
uniform float cSampleLengthWorldSpace; // the sample length in world space in units
uniform float cSampleLengthExponent; // The exponent of the sample length

void main()
{
	const int interleaved = 4;
    const int m = 8;
    const int n = 4;
    const int numSamples = m * n;
    vec2 interleaveOffset = uv * cViewportSize.xy / interleaved;
    vec3 fragmentPosition = texture2D(sMRT2, uv).xyz; // the current fragment in view space
    vec3 fragmentNormal = texture2D(sMRT1, uv).xyz; // the fragment normal
    float rUV = 0; // radius of influence in screen space
    float r = 0; // radius of influence in world space

    if (cSampleInScreenspace == 1)
    {
        rUV = cSampleLengthScreenSpace;
        r = tan(rUV * cFov) * -fragmentPosition.z;
    }
    else
    {
        rUV = atan(cSampleLengthWorldSpace / -fragmentPosition.z) / cFov; // the radius of influence projected into screen space
        r = cSampleLengthWorldSpace;
    }

    if (rUV < cViewportSize.z) // abort if the projected radius of influence is smaller than 1 fragment
    {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        return;
    }

    float accessibility = 0; // accessibility of the fragment

    const vec3 viewVector = vec3(0.0, 0.0, 1.0); // the constant view vector in view space

    // the reflection vector to align the hemisphere with the fragment normal
    // somehow the x component must be flipped...???
    vec3 reflector = normalize(fragmentNormal + viewVector) * vec3(-1.0, 1.0, 1.0); 

    float count = 0;
    float sampleLength;

    for (float i = 0.0f; i < m; i++)
    for (float j = 0.0f; j < n; j++)
    {
        count ++;

        vec2 randomTC = interleaveOffset + vec2(i/(interleaved * m), j/(interleaved * n)); 
        vec3 randomVector = (texture2D(sRand, randomTC) * 2 - 1).xyz; // unpack to [-1, 1]x[-1, 1]x[1, 1]

        sampleLength = pow(count/(numSamples * 1.0), cSampleLengthExponent);

        vec3 sampleVector = reflect(randomVector, reflector) * sampleLength;

        vec2 sampleTC = uv + sampleVector.xy * rUV;

        vec3 samplePosition = texture2D(sMRT2, sampleTC).xyz;

        if (samplePosition.z < (fragmentPosition.z - sampleVector.z * r)) // thin air
            accessibility++;
        else if(length(fragmentPosition - samplePosition) > r) // solid geometry
			accessibility++; // out of reach, i.e. false occluder
    }

    accessibility /= numSamples;
    gl_FragColor = vec4(accessibility, accessibility, accessibility, 1.0);
}
