Directional Light (Per Vertex)

If the light source is very far away, the incoming light rays will be almost parallel, giving rise to directional light. An example of such a light source is the sun.

Diffuse reflection is caused by direct light rays hitting a rough surface. The light is scattered equally in all directions from where it hits:
<surface color by diffuse reflection>
=<light color> * <base color of surface> * cosΘ
=<light color> * <base color of surface> * (<light direction>●<surface normal>)

<surface normal> is a vector representing the direction perpendicular to the surface. The vectors <light direction> and <surface normal> must be normalized unit vectors, which means that their lengths must be 1. Because the normal may be changed by a rotation or a scaling of the object, it needs to be multiplied by the inverse transpose matrix of the model matrix first.

Ambient reflection is caused by indirect light emitted from a combination of light sources and reflected by various surfaces. It may be thought of as the background light. Ambient light models the light that hits an object from all directions with constant intensity:
<surface color by ambient reflection>
=<light color> * <base color of surface>

To render a realistic scene, these two types of reflections need to be combined:
<surface color by diffuse and ambient reflection>
=<surface color by diffuse reflection> + <surface color by ambient reflection>


RESETRUNFULL
// /shared/webgl-library.js
function Vector3(s){
   var i;
   this.entries = new Array();
   if (s && (typeof s)==='object' &&
        s.length && s.length==3){
      for (i=0; i<3; i++) this.entries.push(s[i]);
   } else {
      this.entries=[0,0,0];
   }
}

Vector3.prototype.normalize = function() {
   var v = this.entries;
   var c = v[0], d = v[1], e = v[2],
       g = Math.sqrt(c*c+d*d+e*e);
   v[0] = c/g; v[1] = d/g; v[2] = e/g;
   return this;
};

Matrix4.prototype.inverse = function(){
  var i, s, d, inv, det;
  s = this.entries;
  inv = new Array(16);
  inv[0]  =  s[5]*s[10]*s[15] - s[5]*s[11]*s[14] - s[9]*s[6]*s[15] + s[9]*s[7]*s[14] + s[13]*s[6]*s[11] - s[13]*s[7]*s[10];
  inv[1]  = -s[1]*s[10]*s[15] + s[1]*s[11]*s[14] + s[9]*s[2]*s[15] - s[9]*s[3]*s[14] - s[13]*s[2]*s[11] + s[13]*s[3]*s[10];
  inv[2]  =  s[1]*s[6]*s[15]  - s[1]*s[7]*s[14]  - s[5]*s[2]*s[15] + s[5]*s[3]*s[14] + s[13]*s[2]*s[7]  - s[13]*s[3]*s[6];
  inv[3]  = -s[1]*s[6]*s[11]  + s[1]*s[7]*s[10]  + s[5]*s[2]*s[11] - s[5]*s[3]*s[10] - s[9]*s[2]*s[7]   + s[9]*s[3]*s[6];
  inv[4]  = -s[4]*s[10]*s[15] + s[4]*s[11]*s[14] + s[8]*s[6]*s[15] - s[8]*s[7]*s[14] - s[12]*s[6]*s[11] + s[12]*s[7]*s[10];
  inv[5]  =  s[0]*s[10]*s[15] - s[0]*s[11]*s[14] - s[8]*s[2]*s[15] + s[8]*s[3]*s[14] + s[12]*s[2]*s[11] - s[12]*s[3]*s[10];
  inv[6]  = -s[0]*s[6]*s[15]  + s[0]*s[7]*s[14]  + s[4]*s[2]*s[15] - s[4]*s[3]*s[14] - s[12]*s[2]*s[7]  + s[12]*s[3]*s[6];
  inv[7]  =  s[0]*s[6]*s[11]  - s[0]*s[7]*s[10]  - s[4]*s[2]*s[11] + s[4]*s[3]*s[10] + s[8]*s[2]*s[7]   - s[8]*s[3]*s[6];
  inv[8]  =  s[4]*s[9]*s[15]  - s[4]*s[11]*s[13] - s[8]*s[5]*s[15] + s[8]*s[7]*s[13] + s[12]*s[5]*s[11] - s[12]*s[7]*s[9];
  inv[9]  = -s[0]*s[9]*s[15]  + s[0]*s[11]*s[13] + s[8]*s[1]*s[15] - s[8]*s[3]*s[13] - s[12]*s[1]*s[11] + s[12]*s[3]*s[9];
  inv[10] =  s[0]*s[5]*s[15]  - s[0]*s[7]*s[13]  - s[4]*s[1]*s[15] + s[4]*s[3]*s[13] + s[12]*s[1]*s[7]  - s[12]*s[3]*s[5];
  inv[11] = -s[0]*s[5]*s[11]  + s[0]*s[7]*s[9]   + s[4]*s[1]*s[11] - s[4]*s[3]*s[9]  - s[8]*s[1]*s[7]   + s[8]*s[3]*s[5];
  inv[12] = -s[4]*s[9]*s[14]  + s[4]*s[10]*s[13] + s[8]*s[5]*s[14] - s[8]*s[6]*s[13] - s[12]*s[5]*s[10] + s[12]*s[6]*s[9];
  inv[13] =  s[0]*s[9]*s[14]  - s[0]*s[10]*s[13] - s[8]*s[1]*s[14] + s[8]*s[2]*s[13] + s[12]*s[1]*s[10] - s[12]*s[2]*s[9];
  inv[14] = -s[0]*s[5]*s[14]  + s[0]*s[6]*s[13]  + s[4]*s[1]*s[14] - s[4]*s[2]*s[13] - s[12]*s[1]*s[6]  + s[12]*s[2]*s[5];
  inv[15] =  s[0]*s[5]*s[10]  - s[0]*s[6]*s[9]   - s[4]*s[1]*s[10] + s[4]*s[2]*s[9]  + s[8]*s[1]*s[6]   - s[8]*s[2]*s[5];
  det = s[0]*inv[0] + s[1]*inv[4]+s[2]*inv[8] + s[3]*inv[12];
  if (det === 0) return this;
  for (i = 0; i < 16; i++) s[i] = inv[i] / det;
  return this;}Matrix4.prototype.transpose = function() {
  var e, t;
  e = this.entries;
  t = e[ 1];  e[ 1] = e[ 4];  e[ 4] = t;
  t = e[ 2];  e[ 2] = e[ 8];  e[ 8] = t;
  t = e[ 3];  e[ 3] = e[12];  e[12] = t;
  t = e[ 6];  e[ 6] = e[ 9];  e[ 9] = t;
  t = e[ 7];  e[ 7] = e[13];  e[13] = t;
  t = e[11];  e[11] = e[14];  e[14] = t;
  return this;
};

function initArrayBuffer(gl, attrib, data, num) {
   var b = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, b);
   gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
   var attL = gl.getAttribLocation(gl.program, attrib);
   gl.vertexAttribPointer(attL, num, gl.FLOAT, false, 0, 0);
   gl.enableVertexAttribArray(attL);
}

<!DOCTYPE html><html><head>
<script src="/shared/webgl-library.js"></script>
<script id="vs" type="x-shader/x-vertex">
   attribute vec4 p;  // position
   attribute vec4 c;  // color
   attribute vec4 n;  // normal
   uniform mat4 modelMatrix;
   uniform mat4 viewMatrix;
   uniform mat4 projectionMatrix;
   uniform mat4 normalMatrix;
   uniform vec3 lightColor;
   uniform vec3 lightDirection;
   uniform vec3 ambientLight;
   varying vec4 color;
   void main() {
      gl_Position = projectionMatrix * viewMatrix * modelMatrix * p;
      vec3 normal = normalize(vec3(normalMatrix * n));
      float nDotL = max(dot(lightDirection, normal), 0.0);
      vec3 diffuse = lightColor * c.rgb * nDotL;
      vec3 ambient = ambientLight * c.rgb;
      color = vec4(diffuse+ambient, c.a);
   }
</script>
<script id="fs" type="x-shader/x-fragment">
   #ifdef GL_ES
   precision mediump float;
   #endif
   varying vec4 color;
   void main() {
      gl_FragColor = color;
   }
</script>
<script>
function main() {
   var canvas = document.getElementById('myCanvas');
   var gl = canvas.getContext('webgl');
   initShaders(gl, 'vs', 'fs');
   var n = initVertexBuffers(gl);
   var cvL = gl.getUniformLocation(gl.program, 'lightColor');
   var dvL = gl.getUniformLocation(gl.program, 'lightDirection');
   var avL = gl.getUniformLocation(gl.program, 'ambientLight');
   gl.uniform3f(cvL, 1.0, 1.0, 1.0);
   var dir = new Vector3([0.0,2.0,5.0]);
   dir.normalize();
   gl.uniform3fv(dvL, dir.entries);
   gl.uniform3f(avL, 0.2, 0.2, 0.2);
   var mmL = gl.getUniformLocation(gl.program, 'modelMatrix');
   var vmL = gl.getUniformLocation(gl.program, 'viewMatrix');
   var pmL = gl.getUniformLocation(gl.program, 'projectionMatrix');
   var mm = new Matrix4();
   var vm = new Matrix4();
   var pm = new Matrix4();
   mm.translate(0, 0.4, 0);
   mm.rotate(90, 0, 0, 1);
   pm.perspective(30, canvas.width/canvas.height, 1, 100);
   vm.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);
   gl.uniformMatrix4fv(mmL, false, new Float32Array(mm.entries));
   gl.uniformMatrix4fv(vmL, false, new Float32Array(vm.entries));
   gl.uniformMatrix4fv(pmL, false, new Float32Array(pm.entries));
   var nmL = gl.getUniformLocation(gl.program, 'normalMatrix');
   var nm = new Matrix4(mm.entries);
   nm.inverse();
   nm.transpose();
   gl.uniformMatrix4fv(nmL, false, new Float32Array(nm.entries));
   gl.clearColor(0, 0, 0, 1);
   gl.enable(gl.DEPTH_TEST);
   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
   
function initVertexBuffers(gl) {
   //  v6----- v5   
   //  /|      /|   
   // v1------v0|
   // | |     | |
   // | |v7---|-|v4
   // |/      |/
   // v2------v3

   var vertices = new Float32Array([
     1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0,
     1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0,
     1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0,
    -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0,
    -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0,
     1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0]);
   
   var colors = new Float32Array([
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v3-v4-v5 right
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v5-v6-v1 up
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v1-v6-v7-v2 left
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v7-v4-v3-v2 down
     1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0      // v4-v7-v6-v5 back
  ]);

   var normals = new Float32Array([
      0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,
      1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,
      0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,
     -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,
      0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,
      0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0
   ]);

   var indices = new Uint8Array([
      0, 1, 2,   0, 2, 3,    // front
      4, 5, 6,   4, 6, 7,    // right
      8, 9,10,   8,10,11,    // up
     12,13,14,  12,14,15,    // left
     16,17,18,  16,18,19,    // down
     20,21,22,  20,22,23     // back
   ]);
   
   initArrayBuffer(gl, 'p', vertices, 3);
   initArrayBuffer(gl, 'c', colors, 3);
   initArrayBuffer(gl, 'n', normals, 3);
   gl.bindBuffer(gl.ARRAY_BUFFER, null);
   var b = gl.createBuffer();
   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, b);
   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices, gl.STATIC_DRAW);
   return indices.length;
}</script>
</head>
<body onload="main()">
   <canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>