View Matrix

So far we have been looking at the origin in the negativey-direction. It is possible to move the imaginary camera around so that the object can be viewed from a different perspective. Precisely, the viewing can be defined by 3 vectors:
Veye specifies the location of the camera.
Vtarget specifies the target of the camera.
Vup specifies the rotation of the camera.

The view matrix can be obtained by the following pseudo-code:

mat4 lookAt(vec3 eye, vec3 target, vec3 up){
   vec3 vz=normalize(eye - target);
   vec3 vx=normalize(crossProduct(up,vz));
   vec3 vy=crossProduct(vz,vx);
   inverse=mat4(vec4(vx,0), vec4(vy,0), vec4(vz,0), vec4(eye,1));
   return inverse.getInverse();
}
Pressing the arrow keys moves the camera around. The triangle becomes thin as the camera moves horizontally to the edge.
(Use the full-screen mode for a better testing experience.)

RESETRUNFULL
// /shared/webgl-library.js
Matrix4.prototype.lookAt = function(eyeX, eyeY, eyeZ, targetX, targetY, targetZ, upX, upY, upZ) {
  var e, fx, fy, fz, l, sx, sy, sz, ux, uy, uz;
  fx = targetX - eyeX;
  fy = targetY - eyeY;
  fz = targetZ - eyeZ;  // Normalize f.
  l = Math.sqrt(fx*fx + fy*fy + fz*fz);
  fx /= l;
  fy /= l;
  fz /= l;  // Calculate cross product of f and up.
  sx = fy * upZ - fz * upY;
  sy = fz * upX - fx * upZ;
  sz = fx * upY - fy * upX;  // Normalize s.
  l = Math.sqrt(sx*sx + sy*sy + sz*sz);
  sx /= l;
  sy /= l;
  sz /= l;  // Calculate cross product of s and f.
  ux = sy * fz - sz * fy;
  uy = sz * fx - sx * fz;
  uz = sx * fy - sy * fx;  // A pre-calculated shortcut.
  this.entries[0] = sx;
  this.entries[1] = ux;
  this.entries[2] = -fx;
  this.entries[3] = 0;
  this.entries[4] = sy;
  this.entries[5] = uy;
  this.entries[6] = -fy;
  this.entries[7] = 0;
  this.entries[8] = sz;
  this.entries[9] = uz;
  this.entries[10] = -fz;
  this.entries[11] = 0;
  this.entries[12] = 0;
  this.entries[13] = 0;
  this.entries[14] = 0;
  this.entries[15] = 1;
  this.translate(-eyeX, -eyeY, -eyeZ);
}

<!DOCTYPE html><html>
<head>
<script src="/shared/webgl-library.js"></script>
<script>
var VS_SOURCE=`
   uniform mat4 modelMatrix;
   uniform mat4 viewMatrix;
   attribute vec4 p;
   attribute vec4 c;
   varying vec4 c2;
   void main(){
      c2=c;
      gl_Position = viewMatrix * modelMatrix * p; 
   }`;
var FS_SOURCE=`
   precision mediump float;
   varying vec4 c2;
   void main(){
      gl_FragColor=c2;
   }`;
var eyeX=0,eyeY=0,eyeZ=0.5;var gl,mm,mmL,vm,vmL;
function keydown(ev){
   switch (ev.keyCode){
      case 37: eyeX-=0.01; // left arrow key
      break; case 38: eyeY+=0.01; // up arrow key
      break; case 39: eyeX+=0.01; // right arrow key
      break; case 40: eyeY-=0.01; // down arrow key
   }
   vm.lookAt(eyeX,eyeY,eyeZ, 0,0,0, 0,1,0);
   gl.uniformMatrix4fv(vmL,false, new Float32Array(vm.entries));
}
function draw_colored_triangle(){
   var points=new Array();
   var vc= new Float32Array([ 0.0,-0.5,  1.0, 0.0, 0.0,
                             -0.5, 0.5,  0.0, 1.0, 0.0,
                              0.5, 0.5,  0.0, 0.0, 1.0]);
   var ESIZE=vc.BYTES_PER_ELEMENT;
   var b=gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER,b);
   gl.bufferData(gl.ARRAY_BUFFER,vc,gl.STATIC_DRAW);
   pL=gl.getAttribLocation(gl.program,'p');
   gl.vertexAttribPointer(pL,2,gl.FLOAT,false,ESIZE*5,0);
   gl.enableVertexAttribArray(pL);
   cL=gl.getAttribLocation(gl.program,'c');
   gl.vertexAttribPointer(cL,3,gl.FLOAT,false,ESIZE*5,ESIZE*2);
   gl.enableVertexAttribArray(cL);
   gl.clearColor(0.0,0.0,0.0,1.0);
   gl.clear(gl.COLOR_BUFFER_BIT);
   gl.drawArrays(gl.TRIANGLES,0,3);
}
function start(){
   var canvas = document.getElementById("myCanvas");
   gl = canvas.getContext("webgl");
   initShaders(gl,VS_SOURCE,FS_SOURCE);
   document.onkeydown = function(ev){keydown(ev);};
   mm = new Matrix4();
   mm.translate(0.5,0,0);
   mm.scale(0.5,0.5,0.5);
   mmL = gl.getUniformLocation(gl.program, 'modelMatrix');
   gl.uniformMatrix4fv(mmL,false, new Float32Array(mm.entries));
   vm = new Matrix4();
   vm.lookAt(eyeX,eyeY,eyeZ, 0,0,0, 0,1,0);
   vmL = gl.getUniformLocation(gl.program, 'viewMatrix');
   gl.uniformMatrix4fv(vmL,false, new Float32Array(vm.entries));
   var tick = function(){
      mm.rotate(1,0,0,1);  // rotates one degree every frame
      gl.uniformMatrix4fv(mmL,false, new Float32Array(mm.entries));
      draw_colored_triangle();
      requestAnimationFrame(tick);
   };
   tick();
}
</script>
</head>
<body onload="start()">
   <canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>