#ifdef GL_ES
precision highp float;
#endif

#define SAMPLES 16

uniform sampler2D textureSampler;
uniform sampler2D randomSampler;

uniform float randTextureTiles;
uniform float samplesFactor;
uniform vec3 sampleSphere[16];

varying vec2 vUV;

const vec2 offset1 = vec2(0.0, 0.01);
const vec2 offset2 = vec2(0.01, 0.0);

vec3 normalFromDepth(const float depth, const vec2 coords) {
	float depth1 = texture2D(textureSampler, coords + offset1).r;
	float depth2 = texture2D(textureSampler, coords + offset2).r;

    vec3 p1 = vec3(offset1, depth1 - depth);
    vec3 p2 = vec3(offset2, depth2 - depth);

    vec3 normal = cross(p1, p2);
    normal.z = -normal.z;

    return normalize(normal);
}

void main(void)
{
	const float totalStrength = 1.0;
	const float base = 0.2;
	const float area = 0.0075;
	const float fallOff = 0.000001;
	const float radius = 0.0005;

	vec3 random = texture2D(randomSampler, vUV * randTextureTiles).rgb;
	float depth = texture2D(textureSampler, vUV).r;
	vec3 position = vec3(vUV, depth);
	vec3 normal = normalFromDepth(depth, vUV);
	float radiusDepth = radius / depth;
	float occlusion = 0.0;

	vec3 ray;
	vec3 hemiRay;
	float occlusionDepth;
	float difference;

	for (int i = 0; i < SAMPLES; i++)
	{
		ray = radiusDepth * reflect(sampleSphere[i], random);
		hemiRay = position + dot(ray, normal) * ray;

		occlusionDepth = texture2D(textureSampler, clamp(hemiRay.xy, 0.0, 1.0)).r;
		difference = depth - occlusionDepth;

		occlusion += step(fallOff, difference) * (1.0 - smoothstep(fallOff, area, difference));
	}

	float ao = 1.0 - totalStrength * occlusion * samplesFactor;

	float result = clamp(ao + base, 0.0, 1.0);
	gl_FragColor.r = result;
	gl_FragColor.g = result;
	gl_FragColor.b = result;
	gl_FragColor.a = 1.0;
}
