uniform sampler2D Color0;
uniform sampler2D Normal;
uniform sampler2D Reflection;

uniform float ScreenWidth;
uniform float ScreenHeight;

varying vec3 eyeToScreen;

float radius = 0.05;//0.5;//

vec3 offsetScale = vec3( radius, radius, radius );//radius * vec3( 1.0 / ScreenWidth, 1.0 / ScreenHeight, 1.0 );

vec3 offset00 = vec3(-0.5, 0.5, 0.1 ) * offsetScale;
vec3 offset01 = vec3( 0.5, 2.0,-0.2 ) * offsetScale;
vec3 offset02 = vec3( 1.0, 3.0, 0.3 ) * offsetScale;
vec3 offset03 = vec3(-1.0, 3.0,-0.4 ) * offsetScale;
vec3 offset04 = vec3(-2.0, 2.0, 0.5 ) * offsetScale;

vec3 offset05 = vec3(-1.5, 0.3,-2.1 ) * offsetScale;
vec3 offset06 = vec3(-3.0,-0.5, 3.0 ) * offsetScale;
vec3 offset07 = vec3(-0.5,-1.0,-0.1 ) * offsetScale;
vec3 offset08 = vec3(-1.0,-2.0, 2.1 ) * offsetScale;
vec3 offset09 = vec3(-2.0,-3.0,-1.7 ) * offsetScale;

vec3 offset10 = vec3( 1.0,-0.5, 1.1 ) * offsetScale;
vec3 offset11 = vec3( 0.5,-2.0,-0.5 ) * offsetScale;
vec3 offset12 = vec3( 2.2,-2.2, 2.1 ) * offsetScale;
vec3 offset13 = vec3( 2.5,-0.4,-2.9 ) * offsetScale;
vec3 offset14 = vec3( 2.5, 1.0, 1.1 ) * offsetScale;

const float EPSILON = 0.001;

void main()
{
  // read pixel info from buffer
  vec4 initialColor = texture2D( Color0, gl_TexCoord[0].st );
  vec4 buff         = texture2D( Normal, gl_TexCoord[0].st );

  // unpack
  vec3 norm = 2.0 * buff.xyz - 1.0;
  float depth = buff.w;

  // recover viewspace position
  vec3 receiver;
  receiver.xyz = depth * normalize( eyeToScreen );
  receiver.z   = -receiver.z;
  
  // read plane to reflect offsets
  vec3 plane = texture2D( Reflection, gl_TexCoord[1].st ).xyz;
  plane = 2.0 * plane - 1.0;
  plane = normalize( plane );

  // to store emitter position
  vec3 offset;
  vec3 emitter;
  vec2 emitterCoords;
  vec3 emitterNorm;
  float actualDepth;
  float deltaZ;
  float geometricFactor;
  float distanceSq;
  float incidence;
  float visibility;

  vec3 bounceColor = vec3( 0.0, 0.0, 0.0 );

  // shift + project for lookup
  offset          = reflect( offset00, plane );//offset00;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

  float temp = max( dot(-offset, emitterNorm ), 0.0 );//visibility * incidence;
  vec3 tempV = vec3( emitterCoords.xy, 0.0 );//bounceColor;//offset;//emitterNorm;

    // shift + project for lookup
  offset          = reflect( offset01, plane );//offset01;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset02, plane );//offset02;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset03, plane );//offset03;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset04, plane );//offset04;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset05, plane );//offset05;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset06, plane );//offset06;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset07, plane );//offset07;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset08, plane );//offset08;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset09, plane );//offset09;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset10, plane );//offset10;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset11, plane );//offset11;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset12, plane );//offset12;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset13, plane );//offset13;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

    // shift + project for lookup
  offset          = reflect( offset14, plane );//offset14;//
  emitter         = receiver + offset;
  emitterCoords   = gl_TexCoord[0].st + ( offset.xy / emitter.z );//emitter.xy / emitter.z;//
  buff            = texture2D( Normal, emitterCoords );
  emitterNorm     = 2.0 * buff.xyz - 1.0;
  actualDepth     = buff.w;
  deltaZ          = max( depth - actualDepth, 0.0 );
  geometricFactor = max( dot(-offset, norm ), 0.0 );// *       // angle between reciever surface
                    //max( dot(-offset, emitterNorm ), 0.0 ); // angle between emitter surface
  distanceSq      = deltaZ * deltaZ + dot( offset.xy, offset.xy );
  incidence       = clamp( geometricFactor / (distanceSq + EPSILON), 0.0, 1.0 );
  visibility      = 1.0 - ( 1.0 / ( 1.0 + deltaZ * deltaZ ) );
  bounceColor    += visibility * incidence * texture2D( Color0, emitterCoords ).rgb;

  initialColor.rgb = 3.0 * bounceColor;//vec3( temp, temp, temp );// tempV;// / 5.0;
  
  gl_FragColor = initialColor;
}