text: // Copy files required
  require('!!file?name=/texture.jpg!../static-files/texture.jpg')
  
  //create the rendering context
  var GL = require('../../litegl.js');
  var glm = GL.glmatrix, mat3=glm.mat3, mat4=glm.mat4,
      vec2=glm.vec2, vec3=glm.vec3, vec4=glm.vec4, quat=glm.quat;
  var gl = GL.create({width: window.innerWidth,height: window.innerHeight});
  var container = document.body;
  container.appendChild(gl.canvas);
  gl.animate();
  
  var camera_position = vec3.fromValues(0,100,100);
  
  //build the mesh
  var mesh = GL.Mesh.cube({size:10});
  var plane = GL.Mesh.plane({size:500,xz: true});
  var sphere = GL.Mesh.sphere({size:1});
  
  var texture = GL.Texture.fromURL("texture.jpg", { minFilter: gl.LINEAR});
  
  //create G Buffers
  var w = (gl.canvas.width*0.5)|0;
  var h = (gl.canvas.height*0.5)|0;
  var type = gl.UNSIGNED_BYTE;// , gl.FLOAT, (or gl.HALF_FLOAT_OES although it doesnt work in firefox)
  
  var texture_albedo = new GL.Texture(w,h, { type: type, filter: gl.NEAREST });
  var texture_normal = new GL.Texture(w,h, { type: type, filter: gl.NEAREST });
  var texture_depth = new GL.Texture(w,h, { format: gl.DEPTH_COMPONENT, type: gl.UNSIGNED_INT, filter: gl.NEAREST });
  var textures = [ texture_albedo, texture_normal ];
  var fbo = new GL.FBO( textures, texture_depth );
  var texture_final = new GL.Texture(w,h, { type: type, filter: gl.NEAREST });
  
  //create basic matrices for cameras and transformation
  var proj = mat4.create();
  var view = mat4.create();
  var viewprojection = mat4.create();
  var inv_vp = mat4.create();
  var model = mat4.create();
  var mvp = mat4.create();
  var temp = mat4.create();
  var identity = mat4.create();
  
  
  //set the camera position
  mat4.perspective(proj, 45 * GL.utils.DEG2RAD, gl.canvas.width / gl.canvas.height, 5, 1000);
  mat4.lookAt(view, camera_position, [0,0,0], [0,1,0]);
  
  //basic shader: renders the gbuffers
  var gbuffers_shader = new GL.Shader('\
      precision highp float;\
      attribute vec3 a_vertex;\
      attribute vec3 a_normal;\
      attribute vec2 a_coord;\
      varying vec3 v_wPosition;\
      varying vec3 v_normal;\
      varying vec2 v_coord;\
      uniform mat4 u_mvp;\
      uniform mat4 u_model;\
      void main() {\
          v_coord = a_coord;\
          v_wPosition = (u_model * vec4(a_vertex,1.0)).xyz;\
          v_normal = (u_model * vec4(a_normal,0.0)).xyz;\
          gl_Position = u_mvp * vec4(a_vertex,1.0);\
          gl_PointSize = 20.0;\
      }\
      ', '\
      \
      #extension GL_EXT_draw_buffers : require \n\
      precision highp float;\
      varying vec3 v_wPosition;\
      varying vec3 v_normal;\
      varying vec2 v_coord;\
      uniform vec4 u_color;\
      uniform sampler2D u_texture;\
      void main() {\
        vec3 N = normalize(v_normal);\
        vec4 diffuse_color = u_color * texture2D( u_texture, v_coord );\
        gl_FragData[0] = diffuse_color;\
        gl_FragData[1] = vec4((N * 0.5 + vec3(0.5) ),1.0);\
      }\
      ');
  
  //final shader: computes the light
  var final_fragment_shader = '\
      precision highp float;\
      varying vec2 v_coord;\
      uniform vec3 u_camera_position;\
      uniform vec3 u_light_color;\
      uniform vec3 u_light_position;\
      uniform vec3 u_ambient_light;\
      uniform float u_light_radius;\
      uniform sampler2D u_color_texture;\
      uniform sampler2D u_normal_texture;\
      uniform sampler2D u_depth_texture;\
      uniform mat4 u_invvp;\
      uniform vec4 u_viewport;\
      void main() {\
          vec2 coord = gl_FragCoord.xy / u_viewport.zw;\
          vec3 N = texture2D( u_normal_texture, coord ).xyz * 2.0 - vec3(1.0);\
          N = normalize(N);\
          vec4 diffuse_color = texture2D( u_color_texture, coord );\
          float depth = texture2D( u_depth_texture, coord ).x;\
          if(depth == 1.0) discard;\
          depth = depth * 2.0 - 1.0;\
          vec2 pos2D = coord * 2.0 - vec2(1.0);\
          vec4 pos = vec4( pos2D, depth, 1.0 );\
          pos = u_invvp * pos;\
          pos.xyz = pos.xyz / pos.w;\
          vec3 L = u_light_position - pos.xyz;\
          float dist = length(L);\
          L /= dist;\
          float NdotL = clamp( dot( N, L ), 0.0, 1.0 );\
          float attenuation = 1.0 - pow(dist / u_light_radius,0.8);\
          vec3 final_color = diffuse_color.xyz * (u_light_color * attenuation * NdotL + u_ambient_light);\
          vec3 E = normalize( pos.xyz - u_camera_position );\
          final_color += attenuation * u_light_color * pow( max(0.0, dot( E, reflect(L,N) )), 8.0 ) * 0.5;\
          gl_FragColor = vec4( final_color, 1.0 );\
      }\
      ';
  
  //basic shader
  var final_global_shader = new GL.Shader('\
      precision highp float;\
      attribute vec3 a_vertex;\
      void main() {\
          gl_Position = vec4(a_vertex.xy * 2.0 - 1.0, 0.0, 1.0); \
      }\
      ', final_fragment_shader );
  
  var final_light_shader = new GL.Shader('\
      precision highp float;\
      attribute vec3 a_vertex;\
      uniform mat4 u_mvp;\
      void main() {\
          gl_Position = u_mvp * vec4(a_vertex, 1.0); \
      }\
      ', final_fragment_shader );
  
  //generic gl flags and settings
  gl.clearColor(0.1,0.1,0.1,1);
  gl.enable( gl.DEPTH_TEST );
  
  var uniforms = {
      u_texture: 0,
      u_color: [1.0,0.8,0.8,1],
      u_model: model,
      u_mvp: mvp,
      u_view: view,
      u_camera_position: camera_position,
  };
  
  var objects = [];
  for(var i = 0; i < 20; i++)
  {
      objects.push({
          color: vec3.fromValues( Math.random(), Math.random(), Math.random() ),
          position: vec3.fromValues( Math.random() * 200 - 100 ,0, Math.random() * 200 - 100),
          scale: i == 0 ? 1 : 10 + 10 * Math.random(),
          model: mat4.create(),
          mesh: i == 0 ? plane : sphere
      });
  }
  
  
  var ambient_light = vec3.fromValues( 0.1,0.1,0.2 );
  var global_light_color = vec3.fromValues( 0.9,0.8,0.5 );
  var global_light_position = vec3.fromValues( 80, 50.0, 0.0 );
  
  //create some lights
  var lights = [];
  for(var i = 0; i < 20; i++)
  {
      lights.push({
          color: vec3.fromValues( Math.random(), Math.random(), Math.random() ),
          position: vec3.fromValues( Math.random() * 200 - 100 ,10 + Math.random() * 5, Math.random() * 200 - 100),
          radius: 40,
          model: mat4.create()
      });
  }
  
  //rendering loop
  gl.ondraw = function()
  {
  
      //render something in the texture
      fbo.bind(true);
  
      gl.clearColor(0.1,0.1,0.1,1);
      gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
      gl.enable( gl.DEPTH_TEST );
      gl.enable( gl.CULL_FACE );
      gl.disable( gl.BLEND );
  
      //create modelview and projection matrices
      mat4.lookAt( view, camera_position, [0,0,0], [0,1,0]);
      mat4.multiply( viewprojection, proj, view );
      mat4.invert( inv_vp, viewprojection );
      mvp.set( viewprojection );
  
      texture.bind(0);
  
      for(var i = 0; i < objects.length; i++)
      {
          var object = objects[i];
          mat4.identity( object.model );
          mat4.setTranslation( object.model, object.position );
          mat4.scale( object.model, object.model, [object.scale,object.scale,object.scale] );
          mat4.multiply( mvp, viewprojection, object.model );
          gbuffers_shader.uniforms( uniforms ).draw( object.mesh );
      }
  
      fbo.unbind();
  
      gl.disable( gl.DEPTH_TEST );
  
      texture_final.drawTo(function(){
          //final buffer
          gl.clearColor(0.0,0.0,0.0,1);
          gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
          gl.disable( gl.DEPTH_TEST );
          gl.disable( gl.BLEND );
          var quad = GL.Mesh.getScreenQuad();
  
          var final_uniforms = {
              u_camera_position: camera_position,
              u_invvp: inv_vp,
              u_viewport: gl.viewport_data,
              u_color_texture: 0,
              u_normal_texture: 1,
              u_depth_texture: 2,
              u_light_color: global_light_color,
              u_light_position: global_light_position,
              u_light_radius: 500,
              u_ambient_light: ambient_light
          };
  
          texture_albedo.bind(0);
          texture_normal.bind(1);
          texture_depth.bind(2);
  
          //draw full quad
          final_global_shader.uniforms( final_uniforms ).draw( quad );
  
          //draw point lights
          final_uniforms.u_ambient_light = vec3.create();
          gl.enable( gl.BLEND );
          gl.enable( gl.CULL_FACE );
          gl.blendFunc( gl.ONE, gl.ONE );
          gl.frontFace( gl.CW );
  
          for(var i = 0; i < lights.length; ++i)
          {
              var light = lights[i];
              mat4.identity( light.model);
              mat4.scale( light.model, light.model, [light.radius,light.radius,light.radius] );
              mat4.setTranslation( light.model, light.position );
              final_uniforms.u_light_color = light.color;
              final_uniforms.u_light_position = light.position;
              final_uniforms.u_mvp = mat4.multiply( mvp, viewprojection, light.model );
              final_uniforms.u_light_radius = light.radius;
              final_light_shader.uniforms( final_uniforms ).draw( sphere );
          }
          gl.frontFace( gl.CCW );
      });
  
  
      gl.drawTexture(texture_albedo, 0,0, gl.canvas.width * 0.5, gl.canvas.height * 0.5);
      gl.drawTexture(texture_normal, gl.canvas.width * 0.5,0, gl.canvas.width * 0.5, gl.canvas.height * 0.5);
      gl.drawTexture(texture_depth, 0, gl.canvas.height * 0.5, gl.canvas.width * 0.5, gl.canvas.height * 0.5);
      gl.drawTexture(texture_final, gl.canvas.width * 0.5, gl.canvas.height * 0.5, gl.canvas.width * 0.5, gl.canvas.height * 0.5);
  };
  
  // get mouse actions
  gl.captureMouse();
  gl.onmousemove = function(e)
  {
      if(e.dragging)
      {
          vec3.rotateY( camera_position, camera_position, e.deltax * 0.01 );
          camera_position[1] -= e.deltay * 0.5;
      }
  }
  
  
  //update loop
  gl.onupdate = function(dt)
  {
      var time = GL.utils.getTime() * 0.001;
      for(var i = 0; i < lights.length; i++)
      {
          var light = lights[i];
          light.position[0] = Math.sin( i + time ) * 100;
          light.position[2] = Math.cos( i * 2 + time + 5 ) * 50;
  
      }
  };
  