Divide by W

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 fragment shader determines the color of the final pixel by setting gl_FragColor -->
<script id="vertex-shader" type="x-shader/x-vertex">
  // Each point has a position and color
  attribute vec3 position;
  attribute vec4 color;
  
  // The projection 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;
    
    // First transform the point      
    vec4 transformedPosition = model * vec4( position, 1.0 );
    
    // How much affect does the perspective have?
    float scaleFactor = 0.5;
    
    // Set w by taking the Z value which is typically ranged -1 to 1, then scale
    // it to be from 0 to some number, in this case 0-1.
    float w = (1.0 + transformedPosition.z) * scaleFactor;
    
    // Save the new gl_Position with the custom w component
    gl_Position = vec4( transformedPosition.xyz, w );
  }
</script>

<!-- The vertex shader operates on individual vertices in our model data by setting gl_Position -->
<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'>
  Divide by W
</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,
      ...

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

/*
An easy way to start getting some perspective on our model of the cube is to take the Z coordinate and copy it over to the W coordinate. Normally when converting a cartesian point to homogeneous it becomes (x,y,z,1), but we're going to set it to something like (x,y,z,z). In reality we want to make sure that z is greater than 0 for points in view, so we'll modify it slightly by changing the value to (1.0 + (z * scaleFactor)). This will take a point that is normally in clip space (-1 to 1) and move it into a space more like (0 to 2). The scale factor changes final w value to be either higher or lower overall.

If that sounds a little abstract open up the vertex shader and play around with the scale factor and watch how it shrinks points more towards the surface. Completely change the w component values for really trippy representations of space.

In the next lesson we'll take this step of copying Z into the W slot and turn it into a matrix.

Exercise:

1) Modify the scaleFactor in the vertex shader in index.html and observe the changes.

2) Move the cube around by changing the model matrix.
*/

function CubeDemo () {
  
  // Prep the canvas
  this.canvas = document.getElementById("canvas");
  this.canvas.width = window.innerWidth;
  this.canvas.height = window.innerHeight;
  
  // Grab a context
  this.gl = MDN.createContext(this.canvas);

  this.transforms = {}; // All of the matrix transforms
  this.locations = {}; //All of the shader locations
  
  // Get the rest going
  this.buffers = MDN.createBuffersForCube(this.gl, MDN.createCubeData() );
  this.webglProgram = this.setupProgram();
  
}

CubeDemo.prototype.setupProgram = function() {
  
  var gl = this.gl;
    
  // Setup a WebGL program
  var webglProgram = MDN.createWebGLProgramFromIds(gl, "vertex-shader", "fragment-shader");
  gl.useProgram(webglProgram);
  
  // Save the attribute and uniform locations
  this.locations.model = gl.getUniformLocation(webglProgram, "model");
  this.locations.position =...