Lab: Transfit with Matrices

How to 3D

Chapter 4: Transformation Machinery

Lab: Transfit with Matrices

In this chapter, you've been learning how to express transformations in terms of matrices. Let's apply this knowledge by implementing a Matrix4 abstraction and reimplementing a transformation exercise from an earlier lab using matrices.

Form a group of no more than three, clone the transfit-matrices repository, and run npm install.

Matrix4

Your first challenge is to create a Matrix4 class that you will use to perform transformations. Follow these steps to get it working:

Define in src/matrix.ts a class named Matrix4. Export it so that other files can import it.
Define a parameterless constructor that allocates the matrix's elements as a Float32Array with 16 slots. The Float32Array class is a builtin type in JavaScript's standard library, and it stores 4-byte floats. JavaScript's default number type has double precision, but most graphics cards expect single precision. See MDN to learn the interface of Float32Array.
Your Float32Array is a flat 16-element array, but you want to think of it as a 4×4 two-dimensional array. By the conventions of WebGL, the first four elements are the first column of the matrix. The next four are the second column. This ordering is called column major. In a comment, lay out the numbers 0 through 15 in a 4×4 column major “map” of the matrix. You should be able to look at the map and figure out at which index of the array you can physically find an element at a logical row and column.
Define method Matrix4.get, which accepts a row and a column as parameters and returns the component of the matrix specified by the row and column. To follow mathematical convention, put the row as the first parameter.
Define method Matrix4.set, which accepts a row, a column, and a value as parameters. It sets the component specified by the row and column to the value.
Define method Matrix4.toFloats to return the Float32Array.
Define static method Matrix4.scale. It accepts three parameters for the x-, y-, and z-factors. It returns a matrix that scales by the given factors.
Define static method Matrix4.translate. It accepts three parameters for the x-, y-, and z-offsets. It returns a matrix that translates by the given offsets.
Define static method Matrix4.rotateX. It accepts a number of degrees. It returns a matrix that rotates around the x-axis by the given number of degrees.
Define static method Matrix4.rotateY. It accepts a number of degrees. It returns a matrix that rotates around the y-axis by the given number of degrees.
Define static method Matrix4.rotateZ. It accepts a number of degrees. It returns a matrix that rotates around the z-axis by the given number of degrees.
Define static method Matrix4.identity. It returns the identity matrix. Multiplying a vector or matrix by the identity matrix is akin to multiplying a scalar number by 1. The identity matrix is essentially a scale matrix in which the scale factors are all 1. (Or a translation matrix with offsets of 0. Or a rotation by 0.)
Define Matrix4.multiplyMatrix. It accepts a second Matrix4 as a parameter. Consider calling the parameter that—in contrast to this. It returns the product of this matrix and the parameter matrix as a new Matrix4. Follow this pseudocode:
for each row r of product
  for each column c of product
    d = dot(row r of left, column c of right)
    set element (r, c) of product to d
for each row r of product
  for each column c of product
    d = dot(row r of left, column c of right)
    set element (r, c) of product to d
Define Matrix4.multiplyVector3. It accepts as parameters a Vector3 and a homogeneous coordinate. It transforms the vector by the matrix. Follow this pseudocode:
for each index i
  d = dot(row i of matrix, vector with w)
  set element i of transformed vector to d
for each index i
  d = dot(row i of matrix, vector with w)
  set element i of transformed vector to d
Ensure that all unit tests pass by running npm run test-matrix.

Transforming Renderer

Your second challenge is to repeat the transformation matching exercise from an earlier lab, but this time you transform the shape using your matrices instead of scalar operations. Follow these steps to get your renderer working:

Run npm run start and verify that you can see the pixelated dog and the three outlines.
Modify the vertex shader defined in render in main.ts to accept an additional mat4 uniform. Name it worldFromModel because it transforms from model coordinates into world coordinates, which we'll discuss next chapter. Apply this transformation to the vertex position before clipFromWorld.
Pass the identity transformation to the first draw call. The dog in the bottom-left corner should be restored.
Add three more draw calls to fill the outlines. Pass in different matrices to reshape and reposition the dog.

Submission

To receive credit for your lab work, follow these steps:

Share a screenshot of your renderer in a post on the #lab-gallery channel in Discord. Tag your group members.
Push your code to GitHub.

Only labs submitted on time will be granted credit. Late labs or forgot-to-submits are not accepted because Wednesday at noon is when your instructor has time to grade.

← Lecture: Geometric Modeling