Model Transform

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

by adil_invideo

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 matrix
  uniform mat4 model;

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

  void main() {
    
    //Pass the color down to the fragment shader
    vColor = color;
    
    // Multiply the 
    gl_Position = 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'>
  Model Transform
</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,  1.0, -1.0,
       1.0,  1.0,  1.0,
       1.0, -1.0,  1.0,

      // Left 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
    ];
  
    var colorsOfFaces = [
      [0.3,  1.0,  1.0,  1.0],    // Front face: cyan
      [1.0,  0.3,  0.3,  1.0],    // Back...

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

/*
Placing points directly into clip space is of limited use. What's better is to take model data and transform it into clip space. The cube is an easy example of how to do this. The cube data below consists of vertex positions, the colors of the faces of the cube, and the order of the vertex positions that make up the individual polygons (in groups of 3). The positions and colors are stored in buffers and sent to the shader as attributes, and then operated upon individually.

Finally a single model matrix is set that represents the transformations that will be performed on each position that makes up the model to move it into the correct space. In this case, for every frame of the animation, a series of scale, rotation, and translation matrices move the data into the desired spot in clip space. The cube is the size of clip space (-1,-1,-1) to (1,1,1) so it will need to be shrunk down to fit. This matrix is sent to the shader having been multiplied in JavaScript beforehand.
  
In the shader each position vertex is first transformed into a homogeneous coordinate (vec4), and then multiplied against the model matrix. In

  gl_Position = model * vec4(position, 1.0);

It may be noted that in JavaScript matrix multiplication requires a function, while in the shader it is built into the language with the simple * operator.

At this point the W value of the transformed point is still 1.0. The cube still doesn't have any perspective. The next example will take this setup, and fiddle with the W values to provide some perspective.

Exercise:

1) Shrink down the box using the scale matrix and position it in different places within clip space. Try moving it outside of clip space. Resize the window and watch as the box skews out of shape. Add a rotateZ matrix.

2) Modify the MDN.createCubeData() function in /shared/cube.js to change the underlying data for the cube and note how the model transform perserves it. (Make sure and restore it once you are done for the other...