Specular Lighting

Specular lighting causes a bright spot of light to appear on a surface, making the surface looks shiny.

In the Phong Reflection Model, the intensity of the specular highlight is given by

where R is the mirror reflection of the light vector off the surface, and V is the viewpoint vector. As n increases, the size of the spot decreases.

The middle of the triangle 'lights up', as it reflects the light directly in front of it, making the material looks shiny.
RESETRUNFULL
<!DOCTYPE html><html>
<head>
<script src="/shared/webgl-library.js"></script>
<script id="vs" type="x-shader/x-vertex">
   uniform mat4 u_perspectiveMatrix;
   uniform mat4 u_modelMatrix;
   uniform mat4 u_viewMatrix;
   attribute vec4 a_Position;
   attribute vec3 a_Normal;
   varying vec4 v_Position;
   varying vec3 v_Normal;
   void main() {
      mat4 modelViewMatrix = u_viewMatrix * u_modelMatrix;
      v_Position = modelViewMatrix * a_Position;
      gl_Position = u_perspectiveMatrix * v_Position;
      v_Normal = normalize( mat3(modelViewMatrix) *
                                         a_Normal);
    }
</script>
<script id="fs" type="x-shader/x-fragment">
   #ifdef GL_ES
   precision mediump float;
   #endif
   uniform mat4 u_fViewMatrix;
   uniform vec3 u_lightPosition;
   varying vec4 v_Position;
   varying vec3 v_Normal;
   void main() {
      vec3 normal = normalize(v_Normal);
      vec3 lightPosition = vec3(u_fViewMatrix * vec4(u_lightPosition, 1) - v_Position);
      vec3 lightDir = normalize(lightPosition);
      float lightDist = length(lightPosition);
      float specular = 0.0;
      float d = max(dot(v_Normal, lightDir), 0.0);
      if (d > 0.0) {
         vec3 viewVec = vec3(0,0,1.0);
         vec3 reflectVec = reflect(-lightDir, normal);
        specular = pow(max(dot(reflectVec, viewVec), 0.0), 120.0);
      }
      gl_FragColor.rgb = vec3(0.1,0.1,0.1) + vec3(0.4, 0.4, 0.4) * d + specular;
      gl_FragColor.a = 1.0;
   }
</script>
<script>
var g_perspectiveMatrix = new Matrix4();
var g_modelMatrix = new Matrix4();
var g_viewMatrix = new Matrix4();

function main() {
   var canvas = document.getElementById("myCanvas");
   var gl = canvas.getContext("webgl");
   initShaders(gl, "vs", "fs");
   var perspectiveMatrixShaderLocation = gl.getUniformLocation(gl.program, 'u_perspectiveMatrix');
   var modelMatrixShaderLocation = gl.getUniformLocation(gl.program, 'u_modelMatrix');
   var viewMatrixShaderLocation = gl.getUniformLocation(gl.program, 'u_viewMatrix');
   var lightPositionShaderLocation = gl.getUniformLocation(gl.program, 'u_lightPosition');
   var f_viewMatrixShaderLocation = gl.getUniformLocation(gl.program, 'u_fViewMatrix');
   gl.enable(gl.DEPTH_TEST);
   gl.clearColor(0, 0, 0, 1);
   drawCommon(gl, canvas,
              perspectiveMatrixShaderLocation,
              viewMatrixShaderLocation,
              lightPositionShaderLocation,
              f_viewMatrixShaderLocation);
   drawCube(gl, canvas,
            perspectiveMatrixShaderLocation,
            modelMatrixShaderLocation,
            lightPositionShaderLocation);
}

function drawCommon(gl, canvas,
                    perspectiveMatrixShaderLocation,
                    viewMatrixShaderLocation,
                    lightPositionShaderLocation,
                    f_viewMatrixShaderLocation) {
   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
   g_perspectiveMatrix.perspective(30, canvas.width/canvas.height, 1, 10000);
   g_viewMatrix.lookAt(0, 3, 10,
                       0, 0, 0,
                       0, 1, 0);
   gl.uniformMatrix4fv(perspectiveMatrixShaderLocation, false, g_perspectiveMatrix.entries);
   gl.uniformMatrix4fv(viewMatrixShaderLocation, false, g_viewMatrix.entries);
   gl.uniformMatrix4fv(f_viewMatrixShaderLocation, false, g_viewMatrix.entries);
   var lightPosition = new Float32Array([0, 0, 2]);
   gl.uniform3fv(lightPositionShaderLocation, lightPosition);
}

function drawCube(gl, canvas,
                  perspectiveMatrixShaderLocation,
                  modelMatrixShaderLocation,
                  lightPositionShaderLocation) {
  var vertices = new Float32Array([-1.5,1.5,0.0,
                                    1.5,1.5,0.0,
                                    0.0,-1.5,0.0]);
  var normals = new Float32Array([0.0,0.0,1.0,
                                  0.0,0.0,1.0,
                                  0.0,0.0,1.0]);
  initArrayBuffer(gl,'a_Position',vertices,3);
  initArrayBuffer(gl,'a_Normal',normals,3);
  gl.uniformMatrix4fv(modelMatrixShaderLocation, false, g_modelMatrix.entries);
  gl.drawArrays(gl.TRIANGLES,0,3);
}
</script>
</head>
<body onload="main()">
   <canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>