  'stuff
  ======
  '
  'TRANSFORMS FOR REFLECTION
  'float r1[16], r2[16] 'temp mat4x4
  'float r3[09], r4[09] 'temp mat3x3
  '
  'loadidentity ModelMatrix        'reset
  'loadidentity ModelMatrixInverse 'reset
  'identity NormalMatrix,3       'reset
  '
  ''ModelMatrix = rotate(a, vec3(1.0f, 0.0f, 0.0f)) * rotate(a * 2.0f, vec3(0.0f, 1.0f, 0.0f));
  'mat4x4Scale(ModelMatrix,.5,.5,.5)
  'mat4x4Translate(ModelMatrix,0,0,-3)
  'mat4x4Rotate(ModelMatrix, ang1, 1,0,0)
  'mat4x4Rotate(ModelMatrixInverse, -ang1, 1,0,0)
  'mat4x4Rotate(,ModelMatrix, ang1*2,0,1,0)
  'mat4x4Mul(ModelMatrix,r1,r2)
  '
  ''NormalMatrix = transpose(inverse(mat3x3(ModelMatrix)));
  'Mat4x4ToMat3x3 r3, ModelMatrix
  'InverseMat3x3  r4, r3
  'Transpose      NormalMatrix,r4,3
  '
  'glUniformMatrix4fv(RflUL[2], 1, GL_FALSE, ModelMatrix)
  'glUniformMatrix4fv(RflUL[3], 1, GL_FALSE, ModelMatrixInverse)
  'glUniform1i(RflUL[4], 0)
  '
  'glPushMatrix
  'glGetFloatv GL_MODELVIEW_MATRIX, ModelMatrix
  'glPopMatrix
  'mat4x4Inverse(ModelMatrixInverse, ModelMatrix)
  '
  'glUniformMatrix3fv(RflUL[2], 1, GL_FALSE, NormalMatrix)

  /*
  VERTEX SHADER VERSIONS 3X
  =========================
  uniform mat4 projectionMatrix;  
  uniform mat4 viewMatrix;  
  uniform mat4 modelMatrix;
  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(in_Position, 1.0);

  */

  /*
  ========================
  VERTEX ARRAY INTERLEAVED
  ========================
  '
  glVertexArrayAttribPointer index,size,type,normalised,stride,pointer
  '
  https://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml
  http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_03
  '
  http://ogldev.atspace.co.uk/

  instance rendering
  http://ogldev.atspace.co.uk/www/tutorial33/tutorial33.html
  https://www.opengl.org/wiki/Vertex_Rendering
  http://www.learnopengl.com/#!Advanced-OpenGL/Instancing
  gl_InstanceID?
  http://www.gamedev.net/page/resources/_/technical/opengl/opengl-instancing-demystified-r3226
  http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter03.html
  http://stackoverflow.com/questions/2398028/how-much-does-glcalllist-boost-the-performance
  http://www.gamedev.net/topic/515453-glcalllist-very-slow/

  ================
  GEOMETRY SHADERS
  ================

  https://www.opengl.org/wiki/Geometry_Shader
  https://www.opengl.org/wiki/Geometry_Shader_Examples
  http://www.geeks3d.com/20111111/simple-introduction-to-geometry-shaders-glsl-opengl-tutorial-part1/
  http://www.geeks3d.com/20111117/simple-introduction-to-geometry-shader-in-glsl-part-2/
  http://www.lighthouse3d.com/tutorials/glsl-core-tutorial/geometry-shader/

  ================
  FRAGMENT SHADERS
  ================

  330 output location
  http://stackoverflow.com/questions/9222217/how-does-the-fragment-shader-know-what-variable-to-use-for-the-color-of-a-pixel


  ====================
  TESSELLATION SHADERS
  ====================

  GL_ARB_tessellation_shader

  https://www.opengl.org/wiki/Tessellation
  https://developer.nvidia.com/content/dynamic-hardware-tessellation-basics



  RENDER TO TEXTURE
  =================

  http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/

  // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
  GLuint FramebufferName = 0;
  glGenFramebuffers(1, &FramebufferName);
  glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

  // The texture we're going to render to
  GLuint renderedTexture;
  glGenTextures(1, &renderedTexture);
 
  // "Bind" the newly created texture : all future texture functions will modify this texture
  glBindTexture(GL_TEXTURE_2D, renderedTexture);
 
  // Give an empty image to OpenGL ( the last "0" )
  glTexImage2D(GL_TEXTURE_2D, 0,GL_RGB, 1024, 768, 0,GL_RGB, GL_UNSIGNED_BYTE, 0);
 
  // Poor filtering. Needed !
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

  // The depth buffer
  GLuint depthrenderbuffer;
  glGenRenderbuffers(1, &depthrenderbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER, depthrenderbuffer);
  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, 1024, 768);
  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthrenderbuffer);

  // Set "renderedTexture" as our colour attachement #0
  glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexture, 0);
 
  // Set the list of draw buffers.
  GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
  glDrawBuffers(1, DrawBuffers); // "1" is the size of DrawBuffers

  // Render to our framebuffer
  glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
  glViewport(0,0,1024,768); // Render on the whole framebuffer, complete from the lower left corner to the upper right

  layout(location = 0) out vec3 color;

  -->

  // The fullscreen quad's FBO
  GLuint quad_VertexArrayID;
  glGenVertexArrays(1, &quad_VertexArrayID);
  glBindVertexArray(quad_VertexArrayID);
 
  static const GLfloat g_quad_vertex_buffer_data[] = {
    -1.0f, -1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,
    -1.0f,  1.0f, 0.0f,
    -1.0f,  1.0f, 0.0f,
    1.0f, -1.0f, 0.0f,
    1.0f,  1.0f, 0.0f,
  };
 
  GLuint quad_vertexbuffer;
  glGenBuffers(1, &quad_vertexbuffer);
  glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer);
  glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW);
 
  // Create and compile our GLSL program from the shaders
  GLuint quad_programID = LoadShaders( "Passthrough.vertexshader", "SimpleTexture.fragmentshader" );
  GLuint texID = glGetUniformLocation(quad_programID, "renderedTexture");
  GLuint timeID = glGetUniformLocation(quad_programID, "time");

  // Render to the screen
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
  glViewport(0,0,1024,768); // Render on the whole framebuffer, complete from the lower left corner to the upper right


  -->
  FRAG SHADER

  #version 330 core
 
  in vec2 UV;
 
  out vec3 color;
 
  uniform sampler2D renderedTexture;
  uniform float time;
 
  void main(){
    color = texture( renderedTexture, UV + 0.005*vec2( sin(time+1024.0*UV.x),cos(time+768.0*UV.y)) ).xyz;
  }

  AMBIENT OCCLUSION
  =================

  http://http.developer.nvidia.com/GPUGems3/gpugems3_ch12.html
  https://www.youtube.com/watch?v=hbWV58AH9eg
  http://steamcommunity.com/sharedfiles/filedetails/?id=410329674


  RADIOSITY
  =========

  http://en.wikipedia.org/wiki/Radiosity_%28computer_graphics%29
  https://richardpage.files.wordpress.com/2008/01/car_amb_720p.jpg
  https://richardpage.files.wordpress.com/2008/01/room-radiosity.jpg
  http://www.pwhole.f9.co.uk/brazil.htm
  http://research.microsoft.com/en-us/um/people/sdrucker/papers/radiosity_genexam.pdf
  REALTIME RADIOSITY INTEGRATION
  http://dee.cz/rri/
  http://thomasdiewald.com/blog/?p=2099
Real Time Radiosity / 2006
https://www.youtube.com/watch?v=FB8Nu8_rsDM
-->
Realtime Voxel Cone Tracing Global Illumination
https://www.youtube.com/watch?v=EHaSzQTjlik
-->
Future computing speculation - next next gen
https://www.youtube.com/watch?v=ys0pnvlcKO8
Euclideon Makes World’s Most Realistic Graphics
https://www.youtube.com/watch?v=5AvCxa9Y9NU
https://www.youtube.com/watch?v=DbMpqqCCrFQ
-->2011
https://www.youtube.com/watch?v=JVB1ayT6Fdc

webgl pathtrace
http://madebyevan.com/


  SHADOW MAPPING
  ==============
  http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/
  http://ogldev.atspace.co.uk/www/tutorial24/tutorial24.html
  http://ogldev.atspace.co.uk/www/tutorial23/tutorial23.html
  render to texture
  http://www.nvidia.com/object/doc_shadows.html
  http://developer.download.nvidia.com/SDK/9.5/Samples/samples.html#HLSL_SoftShadows
  -->
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
 GLuint FramebufferName = 0;
 glGenFramebuffers(1, &FramebufferName);
 glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);
 
 // Depth texture. Slower than a depth buffer, but you can sample it later in your shader
 GLuint depthTexture;
 glGenTextures(1, &depthTexture);
 glBindTexture(GL_TEXTURE_2D, depthTexture);
 glTexImage2D(GL_TEXTURE_2D, 0,GL_DEPTH_COMPONENT16, 1024, 1024, 0,GL_DEPTH_COMPONENT, GL_FLOAT, 0);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
 glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0);
 
 glDrawBuffer(GL_NONE); // No color buffer is drawn to.
 
 // Always check that our framebuffer is ok
 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
 return false;
 -->
 glm::vec3 lightInvDir = glm::vec3(0.5f,2,2);
 
 // Compute the MVP matrix from the light's point of view
 glm::mat4 depthProjectionMatrix = glm::ortho<float>(-10,10,-10,10,-10,20);
 glm::mat4 depthViewMatrix = glm::lookAt(lightInvDir, glm::vec3(0,0,0), glm::vec3(0,1,0));
 glm::mat4 depthModelMatrix = glm::mat4(1.0);
 glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrix * depthModelMatrix;
 
 // Send our transformation to the currently bound shader,
 // in the "MVP" uniform
 glUniformMatrix4fv(depthMatrixID, 1, GL_FALSE, &depthMVP[0][0])
 -->
 VERTEX SHADER
 #version 330 core
 
 // Input vertex data, different for all executions of this shader.
 layout(location = 0) in vec3 vertexPosition_modelspace;
 
 // Values that stay constant for the whole mesh.
 uniform mat4 depthMVP;
 
 void main(){
  gl_Position =  depthMVP * vec4(vertexPosition_modelspace,1);
 }
 FRAGMENT SHADER
 #version 330 core
 
 // Ouput data
 layout(location = 0) out float fragmentdepth;
 
 void main(){
  // Not really needed, OpenGL does it anyway
  fragmentdepth = gl_FragCoord.z;
 } 
 -->
 glm::mat4 biasMatrix(
 0.5, 0.0, 0.0, 0.0,
 0.0, 0.5, 0.0, 0.0,
 0.0, 0.0, 0.5, 0.0,
 0.5, 0.5, 0.5, 1.0
 );
 glm::mat4 depthBiasMVP = biasMatrix*depthMVP;
 -->
 VERTEX SHADER 2
 // Output position of the vertex, in clip space : MVP * position
 gl_Position =  MVP * vec4(vertexPosition_modelspace,1);
 
 // Same, but with the light's view matrix
 ShadowCoord = DepthBiasMVP * vec4(vertexPosition_modelspace,1);
 -->
 float visibility = 1.0;
 if ( texture( shadowMap, ShadowCoord.xy ).z  <  ShadowCoord.z){
  visibility = 0.5;
 } 
 color =
 // Ambient : simulates indirect lighting
 MaterialAmbientColor +
 // Diffuse : "color" of the object
 visibility * MaterialDiffuseColor * LightColor * LightPower * cosTheta+
 // Specular : reflective highlight, like a mirror
 visibility * MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5);
 -->
 CORRECTION FOR SHADOW ACNE
 float bias = 0.005;
 float visibility = 1.0;
 if ( texture2D( shadowMap, ShadowCoord.xy ).z  <  ShadowCoord.z-bias){
  visibility = 0.5;
 }
 -->
 float bias = 0.005*tan(acos(cosTheta)); // cosTheta is dot( n,l ), clamped between 0 and 1
 bias = clamp(bias, 0,0.01);
 -->
 // We don't use bias in the shader, but instead we draw back faces,
        // which are already separated from the front faces by a small distance
        // (if your geometry is made this way)
        glCullFace(GL_FRONT); // Cull front-facing triangles -> draw only back-facing triangles

 -->
 glCullFace(GL_BACK); // Cull back-facing triangles -> draw only front-facing triangles




  DISPLACEMENT MAPPING
  ====================
  http://www.ozone3d.net/tutorials/vertex_displacement_mapping_p03.php
  -->
  Chapter 8. Per-Pixel Displacement Mapping with Distance Functions
  http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter08.html
  -->
  http://gamedev.stackexchange.com/questions/74354/per-pixel-displacement-mapping-glsl
  http://sirkan.iit.bme.hu/~szirmay/egdisfinal3.pdf
  -->
  http://www.academia.edu/4813603/Fast_Per-Pixel_Displacement_Mapping_with_Screen-Space_Depth_Interval_Grids
  -->
  PPDM
  -->
  DirectX 11 - Displacement Mapping
  https://www.youtube.com/watch?v=FWwUp7Oz1Lw
  -->
  Creating Displacement Maps
  https://www.youtube.com/watch?v=d0f63wh0P1o
  -->
  How to Conform Text to a Surface Using a Displacement Map in Photoshop
  https://www.youtube.com/watch?v=huvysaySBrw
  How to Use Displacement Maps in Photoshop
  https://www.youtube.com/watch?v=-EKQyyufxlM

*/