6.6 - A Matrix Library - GlMatrix4x4

Fundamental to all 3D computer graphics is the 4x4 matrix transform. In the original OpenGL API, all basic transformations were implemented for you in the library’s code. All you had to do was call the correct function in the correct order. WebGL was implemented for low power mobile devices with limited CPU and GPU functionality. Transformation matrix functionality was not included in the WebGL API. Therefore, you have to implement your own matrix operations in JavaScript. But it doesn’t make sense for individual programmers to “re-invent the wheel”. This lesson presents you with a JavaScript matrix library and explains how to use it.

WebGL Transformation Matrices

A WebGL, 4x4, transformation matrix is a 1D array of type Float32Array which contains 16 floating point values. The values represent a 2D array that are stored in column-major order. (Back in the 1960’s, Fortran stored 2-dimensional data in column-major order. That convention has propagated to various system still in use today, including OpenGL. Most modern programming languages use row-major order.)

The 4x4 transformation matrix:

0
4
8
12
1
5
9
13
2
6
10
14
3
7
11
15
Eq1

would be created in JavaScript code like this:

var matrix = new Float32Array([0,4,8,12, 1,5,9,13, 2,6,10,14, 3,7,11,15]);
// Or
var m = new Float32Array(16);
m[0] =  0;  m[4] =  1;  m[ 8] =  2;  m[12] = 3;
m[1] =  4;  m[5] =  5;  m[ 9] =  6;  m[13] = 7;
m[2] =  8;  m[6] =  9;  m[10] = 10;  m[14] = 11;
m[3] = 12;  m[7] = 13;  m[11] = 14;  m[15] = 15;

In most cases the 2nd example above is used because it is easier to debug the code if you can visualize the data values as a 2D array.

Note: If you re-format the matrix library code you will lose the “multiple statements per line” formatting which helps to visualize the 4x4 matrices. It is recommended that you do not re-format this code file.

Design Decisions for a Matrix Library

A typical class definition defines a set of data and a set of functions that act on that data. One of the important ideas behind classes is the encapsulation and protection of a set of data values inside an instance of the class. We don’t really need data protection for our matrix library. What we need is encapsulation of the functionality of matrix operations so that we can minimize the creation and deletion of scratch arrays that are needed for matrix processing. Consider that an animation requires the rendering of a scene at least 30 times per second. If you are constantly creating new object instances every time you render, you will be creating a lot of objects. JavaScript does dynamic memory garbage collection, so many programmers simply ignore memory issues. But if you can minimize the creation of new objects for each rendering, your animations have the potential to run more smoothly.

The GlMatrix4x4 Class

A class called GlMatrix4x4 is defined in a file named glmatrix4x4.js. It encapsulates the matrix functionality we need to produce WebGL renderings. A GlMatrix4x4 object does not store a matrix. It encapsulates matrix functionality and the scratch arrays needed for that functionality. And, by separating matrix functionality from matrix data, the syntax of the code is simplified.

You can typically create one instance of the library and use it for your entire program. The library contains functions that:

  • create transforms,
  • set the values of a specific type of transform, and
  • perform matrix operations.

A matrix transform is stored as a Float32Array. Four functions in an GlMatrix4x4 object create and return new transforms and their names all start with create. These functions should not be called in your rendering code for every animation frame. They should be called once in your setup code to create any transforms you need during rendering. The four functions that create a new matrix transform are:

  • create(), which creates and returns a new 4x4 transformation matrix.
  • createOrthographic(), which creates a new orthographic projection transformation matrix.
  • createPerspective(), which creates a new perspective projection transformation matrix.
  • createFrustum(), which creates a new perspective projection transformation matrix.

Functions that set the values of a transformation matrix must send a transformation matrix as the first parameter. For example:

  • scale(M, sx, sy, sz) sets M to a scale transform.
  • translate(M, tx, ty, tz) sets M to a translation transform.
  • rotate(M, angle, x_axis, y_axis, z_axis) sets M to a rotation transform.

Functions that perform matrix calculations change the value of their first parameter, while leaving all of the other parameters unchanged. The parameters are ordered similar to assignment statements which always change their left-hand side variable, but leave all value on the right-hand-side of an assignment statement unchanged. For example:

  • multiply(R, A, B) sets R to the product of A times B. (R = A*B)

The function multiplySeries() will multiply any number of matrices together to produce a single transformation matrix. It uses variable arguments and will accept as many arguments as you send it. For example, m.multiplySeries(R,A,B,C,D,E) will calculate the matrix product of A*B*C*D*E and store the result in R. In equation format, it performs R = A*B*C*D*E;. The order of the multiplications is critical. If the transform R is applied to a set of vertices, the effect of R would be that

  1. transform E was applied to a vertex,
  2. then transform D was applied to the transformed vertex,
  3. then transform C was applied to the transformed vertex,
  4. then transform B was applied to the transformed vertex, and
  5. finally transform A was applied to the transformed vertex.

When you create a single transform from multiple transforms, you must always order the transformations from right to left.

The GlMatrix4x4 Code

The WebGL program below displays the GlMatrix4x4 class code. Please study the GlMatrix4x4 class to get familiar with its matrix functionality. (Hide the canvas to better review the code.) The translate_scene.js code demonstrates how to use the matrix functionality in a WebGL program. Notice that the creation of all matrices is done once in the scene’s constructor. The matrices are then used repeatedly in the scene’s rendering function. Since there are no new objects created for each rendering, garage collection is minimized.

Show: Code   Canvas   Run Info
../_static/06_example05/translate.html

An example of translating a model.

Please use a browser that supports "canvas"
Animate
X translation 0.00 : -2.0 2.0
Y translation 0.00 : -2.0 2.0
Z translation 0.00 : -2.0 2.0

Show: Process information    Warnings    Errors
Open this webgl program in a new tab or window

HTML Code To Use GlMatrix4x4

The GlMatrix4x4 class uses code from two other classes:

  • glpoint4.js, which defines a class for (x,y,z,w) points.
  • glvector3.js, which defines a class for <dx,dy,dz> vectors.

These files must be loaded into your browser along with the matrix library. To use the matrix library include <script> directives in your HTML file that look something like this:

<script src="../learn_webgl/glpoint4.js"></script>
<script src="../learn_webgl/glvector3.js"></script>
<script src="../learn_webgl/glmatrix4x4.js"></script>

Change the file paths based on the relative location of your JavaScript code files to your HTML file. If the HTML and JavaScript files are in the same folder on the server, you can omit a file path.

Glossary

code library
a set of common functionality gathered into a single place. It is standard practice to put the functionality into a single class, or a group of classes.
column-major order
values in a 2-dimensional array are store in a 1D array and organized by columns. (All computer memory is 1-dimensional; multi-dimensional arrays are always stored in computer memory as 1D arrays in some agreed upon order.)

Self Assessment

For all of these questions, assume that m is an instance of the GlMatrix4x4 class.

    Q-138: How many instances of the GlMatrix4x4 do you typically need to create for a WebGL program?
  • One
  • Correct.
  • Four
  • Incorrect. Where did four come from?
  • One for each 4x4 transform you need in a program.
  • Incorrect. No, a GlMatrix4x4 contains functionality, not matrices.
  • Two for each 4x4 transform you need in a program.
  • Incorrect. No, a GlMatrix4x4 contains functionality, not matrices.

    Q-139: You have created an instance of the GlMatrix4x4 class called m and you want to use it to create a 4x4 transformation matrix called sam. Which of the following code examples accomplish this?

  • sam = m.create();
  • Correct.
  • sam = new GlMatrix4x4().create();
  • Incorrect. (While this will work in theory, it creates an unnecessary GlMatrix4x4 object.)
  • sam = new GlMatrix4x4();
  • Incorrect. This creates an instance of the GlMatrix4x4 class.
  • m.create();
  • Incorrect. It creates a new 4x4 transformation matrix but does not assign it to a variable so it can be used.

    Q-140: You need to multiple 3 matrices together, a times b times c, and store the results in matrix q. You can do this using the multiplySeries() function like this:

    m.multiplySeries(q,a,b,c);
    

    Which of the parameters to the multiplySeries() function are modified by the function call? (Select all that apply.)

  • matrix q
  • Correct. Only the first parameter is modified and contains the results of the matrix multiplications.
  • matrix a
  • Incorrect.
  • matrix b
  • Incorrect.
  • matrix c
  • Incorrect.
    Q-141: Which of the following statements set a matrix called mary to a scaling transformation that doubles only the x coordinates of a model?
  • m.scale(mary, 2, 1, 1);
  • Correct. The x scale factor is 2, while the scale factors of 1 for the y and z components leaves them unchanged.
  • m.scale(mary, 2, 2, 2);
  • Incorrect. This doubles the x, y and z components of all vertices.
  • m.scale(mary, 2);
  • Incorrect. This does not provide the 3 scaling parameters required by the function call.
  • m.scale(mary, 1, 1, 2);
  • Incorrect. This doubles the z components, leaving the x and y components unchanged.
    Q-142: Which of the following statements set a matrix called nice to a rotation transformation that rotates a model 30 degrees about the y axis?
  • m.rotate(nice, 30, 0, 1, 0);
  • Correct. The angle is 30 degrees and the axis of rotation is the y axis, <0,1,0>.
  • m.rotate(nice, 30, 1, 0, 0)
  • Incorrect. This rotates about the x axis, <1,0,0>.
  • m.rotate(nice, 30, 0, 0, 1);
  • Incorrect. This rotates about the z axis, <0,0,1>.
  • m.rotate(nice, 30);
  • Incorrect. This is missing the 3 parameters that define the axis of rotation.
Next Section - 6.7 - Using GlMatrix4x4 - A Robot Arm