Animated Cubes Tutorial

Introduction JavaScript Vertex Shader Fragment Shader Summary

Introduction

This short tutorial explains how to implement five animated WebGL cubes which change colors, rotate, and animate. Topics include some JavaScript, the vertex and fragment shaders. JavaScript loads the cubes, saves the location of shader uniforms, initializes and renders the cubes. The shader uniform, u_color, eventually assigns color to each cube.

JavaScript

JavaScript loads one cube model, copies the model to five positions, saves the location of a vec4 uniform from the shader, then uploads random colors to the uniform. Random colors upload to the uniform after the user taps the Change Colors button or the canvas.

Cube Vertices

The following listing includes an array of vertices representing a cube of 1.5 units in length, width, and depth. The cube's centered at origin (0,0,0).

arrayVertices = (
 [

  -0.75, -0.75, 0.75,     
  0.75, -0.75, 0.75,
  -0.75, 0.75, 0.75,   
  
  -0.75, 0.75, 0.75,    
  0.75, -0.75, 0.75,
  0.75, 0.75, 0.75, 
  

  0.75, -0.75, 0.75,    
  0.75, -0.75, -0.75,
  0.75, 0.75, -0.75,          
    
  0.75, 0.75, 0.75,    
  0.75, -0.75, 0.75,
  0.75, 0.75, -0.75,


  -0.75, 0.75, 0.75,     
  0.75, 0.75, 0.75, 
  -0.75, 0.75, -0.75,
  
  -0.75, 0.75, -0.75,      
  0.75, 0.75, 0.75,   
  0.75, 0.75, -0.75, 
  

  0.75, 0.75, -0.75,    
  0.75, -0.75, -0.75,
  -0.75, 0.75, -0.75,
  
  0.75, -0.75, -0.75,    
  -0.75, -0.75, -0.75,
  -0.75, 0.75, -0.75,
    

  -0.75, 0.75, -0.75,    
  -0.75, -0.75, -0.75,
  -0.75, 0.75, 0.75,
   
  -0.75, -0.75, -0.75,    
  -0.75, -0.75, 0.75, 
  -0.75, 0.75, 0.75,     
    
  
  0.75, -0.75, -0.75,    
  0.75, -0.75, 0.75, 
  -0.75, -0.75, -0.75,
  0.75, -0.75, 0.75,     
  -0.75, -0.75, 0.75,
  -0.75, -0.75, -0.75,    
 ]);

Initialize Each Cube

The following listing prepares each cube's Entity matrix, red, green, and blue color channels. An Entity represents one sprite, with values for color, plus a matrix for location and rotation. The properties r, g, and b, represent a unique combination of red, green, and blue, for each cube. Call JavaScript method, Math.random(), to assign each channel random values between 1.0 and 0.0. Each cube begins rendering with a random color.

Prepare each cube's matrix to move the cube along the X, Y, and Z axes. Number, nOffset, equals two. Number, N_Z, equals negative ten. Each cube begins rendering at a different location on the display screen. For example, the first cube renders at X = 2, Y = -2, and Z = -10. The following listing initailizes color channels and matrices for all five cubes.

function Entity(n){
 
 this.r = Math.random();
 this.g = Math.random();
 this.b = Math.random();  
  
 this.rad = nRadian;  
 switch(n){
  case 0:
   this.matrix = matrixTranslationXYZ
   (
    MATRIX_DEFAULT,
    nOffset,
    -nOffset,
    N_Z
   );
    
   this.r = 0.0;
   this.g = 0.0;
   this.b = 1.0;       
   break;
   
  case 1:    
   this.matrix = MATRIX_DEFAULT;
   this.matrix[14] = N_Z;
   break;
   
  case 2:
   this.matrix = matrixTranslationXYZ
   (
    MATRIX_DEFAULT,
    nOffset,
    nOffset,
    N_Z
   );           
   break;
   
  case 3:
   this.matrix = matrixTranslationXYZ
   (
    MATRIX_DEFAULT,
    -nOffset,
    -nOffset,
    N_Z
   );               
  break;
  
  case 4:
   this.matrix = matrixTranslationXYZ
   (
    MATRIX_DEFAULT,
    -nOffset,
    nOffset,
    N_Z
   );       
   break;
  }   
  return this;  
}

Change Cube Colors

Function animColors() activates when the user taps the canvas or the Change Colors button. Array, aEntities, includes a set of entities, where each entity represents a cube. Number, N_ENTITIES, equals the length of array, aEntities. For each cube, assign random red, green, and blue, color channels, as follows.

function animColors(){
 for (var i = 0; i < N_ENTITIES; i++){
  var e = aEntities[i];
  e.r = Math.random();
  e.g = Math.random();
  e.b = Math.random();  
 }
 drawScene();
}

Location of Color Uniform

The fragment shader declares a uniform named, u_color. The uniform's a vec4. That means the uniform holds four floating point values. The values can represent red, green, blue, and alpha channels. An alpha value of one is completely opaque. The source code in this tutorial assigns values to all four channels, or floating point numbers. Alpha always equals one in this tutorial.

From JavaScript, call the WebGL API method getUniformLocation(). The first parameter is the WebGL program. The second parameter is the name of the uniform, as declared in the shader. Method getUniformLocation() saves the fragment shader's uniform, u_color, to JavaScript variable, uColor, in the following listing.

uColor = gl.getUniformLocation(
 program, 
 "u_color"
);  

Render the Cubes

The following listing uploads each cube's unique red, green, and blue color channel. Notice the alpha value always equals 1. The variable, e, references one entity.

gl.uniform4f(
 uColor, 
 e.r, 
 e.g, 
 e.b, 
 1
);

Five cubes are saved to the array named, aEntities. The variable, e, references one entity. A switch statement modifies the rotation and Z coordinate of each cube's model matrix. Upload each cube's model matrix as follows.

gl.uniformMatrix4fv
(
 uMatrixTransform, 
 false, 
 new Float32Array
 (
  e.matrix
 )
);

Last call the WebGL method drawArrays(), for each cube. Draw one full cube, begining at the first vertex, index 0, and ending at the last vertex, nVLength. Number nVLength stores the length of all vertices in a cube.

gl.drawArrays
(
 gl.TRIANGLES, 
 0, 
 nVLength
);

Complete Render Function

The rendering function for all five cubes follows.

function drawScene() {   
 for (var i = 0; i < N_ENTITIES; i++){
  var e = aEntities[i];
  gl.uniform4f(uColor, e.r, e.g, e.b, 1);
  switch(i){
  
  case 0:
   e.matrix = matrixRotationZ
   (
    e.matrix,
    e.rad
   );
   break;
   
  case 1:
  case 3:
   e.matrix = matrixRotationX
   (
    e.matrix,
    e.rad
   );
   break;
   
  case 2:
   e.matrix = matrixRotationY
   (
    e.matrix,
    e.rad
   );
   break;
   
  case 4:
   e.matrix = matrixRotationZ
   (
    e.matrix,
    e.rad
   );
   break;      
  }
     
  e.matrix[14] = nZ;
  
  gl.uniformMatrix4fv
  (
   uMatrixTransform, 
   false, 
   new Float32Array
   (
    e.matrix
   )
  );
  
  gl.drawArrays
  (
   gl.TRIANGLES, 
   0, 
   nVLength
  );
  
  e.rad += N_RAD;
  
  if(bAway == true){
   nZ -= 0.1;
   if (nZ < -64)
     bAway = false;
  }
  else {
   nZ += 0.1;
    if (nZ > -8)
     bAway = true;
  }     
 }    
}  

Vertex Shader

The vertex shader multiples the current vertex, a_position, by the perspective projection matrix, um4_pmatrix, and the model matrix, um4_matrix. Assign the result to built in variable gl_Position. Each point renders with coordinates modified by perspective, translation, and rotation.

attribute vec3 a_position;
    
uniform mat4 um4_matrix;

uniform mat4 um4_pmatrix;
    
void main(void) {
 gl_Position = 
 um4_pmatrix * um4_matrix * 
  vec4(
  a_position, 
  1.0
 );        
}

Fragment Shader

The JavaScript section described saving a reference to the fragment shader's vec4 u_color. The following listing demonstrates u_color in action. Assign u_color to the built in variable gl_FragColor. Each rendered fragment of a cube displays the color assigned to u_color.

precision mediump float;

uniform vec4 u_color;
     
void main(void) {
  gl_FragColor = u_color;
}

Summary

This short tutorial explained how to implement five animated WebGL cubes which change colors, rotate, and animate. Topics included some JavaScript, the vertex and fragment shaders. JavaScript loads the cubes, saves the location of shader uniforms, initializes and renders the cubes. The shader uniform, u_color, eventually assigns color to each cube.

Copyright © 2015 Seven Thunder Software. All Rights Reserved.