Perspective Matrix

This fiddle is part of the MDN content kit for WebGL Model View Projection. https://github.com/TatumCreative/mdn-model-view-projection

by tatumcreative

HTML

<!-- The vertex shader operates on individual vertices in our model data by setting gl_Position -->
<script id="vertex-shader" type="x-shader/x-vertex">
  //Each point has a position and color
  attribute vec3 position;
  attribute vec4 color;

  // The transformation matrices
  uniform mat4 model;
  uniform mat4 projection;

  // Pass the color attribute down to the fragment shader
  varying vec4 vColor;

  void main() {
    
    // Pass the color down to the fragment shader
    vColor = color;
    
    // Read the multiplication in reverse order, the point is taken from
    // the original model space and moved into world space. It is then
    // projected into clip space as a homogeneous point. Generally the
    // W value will be something other than 1 at the end of it.
    gl_Position = projection * model * vec4( position, 1.0 );
  }
</script>

<!-- The fragment shader determines the color of the final pixel by setting gl_FragColor -->
<script id="fragment-shader" type="x-shader/x-fragment">
  precision mediump float;
  varying vec4 vColor;
  
  void main() {
    gl_FragColor = vColor;
    // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
</script>
  
<canvas id="canvas"></canvas>
  
<h1 class='lesson-title'>
  Perspective Matrix
</h1>

<script>
  //Shared code for the examples
    
    
  // Define the MDN global
  var MDN = {};

  // Define the data that is needed to make a 3d cube
  MDN.createCubeData = function() {
  
    var positions = [
      // Front face
      -1.0, -1.0,  1.0,
       1.0, -1.0,  1.0,
       1.0,  1.0,  1.0,
      -1.0,  1.0,  1.0,

      // Back face
      -1.0, -1.0, -1.0,
      -1.0,  1.0, -1.0,
       1.0,  1.0, -1.0,
       1.0, -1.0, -1.0,

      // Top face
      -1.0,  1.0, -1.0,
      -1.0,  1.0,  1.0,
       1.0,  1.0,  1.0,
       1.0,  1.0, -1.0,

      // Bottom face
      -1.0, -1.0, -1.0,
       1.0, -1.0, -1.0,
       1.0, -1.0,  1.0,
      -1.0, -1.0,  1.0,

      // Right face
       1.0, -1.0, -1.0,
       1.0, ...

CSS

html, body {
	width:100%;
	height:100%;
	margin: 0;
}
canvas {
	width: 100% !important;
	height: 100% !important;
}
.lesson-title {
  position: absolute;
  bottom: 0;
  left: 0;
  margin: 0.7em;
  font-family: sans-serif;
  font-size: 1em;
  color: #888;
  font-weight: normal;
}
.lesson-title a {
	text-decoration: none;
	color: #668;
}
.lesson-title a:hover {
	color: #338;
}

JavaScript

/*
Up to this point we've proceeded step by step to create our own 3d rendering setup. However the current rig has some issues. For one it gets skewed whenever we resize our window. Another is that our simple projection doesn't handle a wide range of values for the scene data. Most scenes don't work in clip space and can have a wide range of values. It would be helpful to define what distance is relevant to the scene so that precision isn't lost in converting the numbers. Finally it's very helpful to have a fine-tuned control over what points get placed inside and outside of clip space.  In the previous examples the corners of the cube in fact occasionally get clipped.

The perspective matrix is a type of projection matrix that accomplishes all of these requirements. The math also starts to get a lot more complicated and won't be explained in these examples. In short it combines dividing by W like was done with the previous examples plus some ingenious trigonometry manipulations. For more information about the math behind it check some of the following links:

  http://www.songho.ca/opengl/gl_projectionmatrix.html
  http://ogldev.atspace.co.uk/www/tutorial12/tutorial12.html
  http://stackoverflow.com/questions/28286057/trying-to-understand-the-math-behind-the-perspective-matrix-in-webgl/28301213#28301213

One important thing to note about the perspective matrix used below is that it flips the z axis. In clip space the z+ goes away from the viewer, while with this matrix it comes towards the viewer.


The MDN.perspectiveMatrix() function takes 4 arguments.

fieldOfViewInRadians:
		
This represents the angle of how much is in view in the scene. The larger the number is, the more that is visible by the camera. The geometry at the edges becomes more and more distorted. This is equivalent to a wide angle lens. When the field of view is larger the objects typically get smaller.

When the field of view is smaller, then the camera can see less and less in the scene. The...