Speaking of matrix, it is easy to remind people of linear algebra and discrete mathematics that we once could not learn, but as a core part of graphic development, it represents every movement and transformation, just like fish can not get out of water, matrix is not a topic that can be avoided.

The good news is that three.js helps us encapsulate a lot of matrix operations into some top-level methods and provides an excellent math library that we don’t necessarily need to know how to calculate, just how to use, to get most of what we want.

This article will show you how to use matrices and vectors in three. js without knowing the internal structure.

Let’s start with an example

Let me give you a quick example of why we use the matrix method before we get into some boring concepts.

This is what we’re going to end up with.

First, we will create three geometers:

var box_geometry = new THREE.BoxGeometry();
var sphere_geometry = new THREE.SphereGeometry(0.5.32.32);
var cylinder_geometry = new THREE.CylinderGeometry(0.1.0.1.0.5);

var material = new THREE.MeshLambertMaterial({color: new THREE.Color(0.9.0.55.0.4)})
Copy the code

The three geometers are box, ball and cylinder.

Then create three grids and place them into the scene.

var box = new THREE.Mesh(box_geometry, material);
var sphere = new THREE.Mesh(sphere_geometry, material);
sphere.position.y += 1;
var cylinder = new THREE.Mesh(cylinder_geometry, material);
cylinder.position.y += 1.75;
scene.add(box);
scene.add(sphere);
scene.add(cylinder);
Copy the code

This code generates the following scenario:

It’s not pretty, but it’s good enough for example, and now I want the stack to be half the size. I usually cut the scale property of the object in half, like this:

box.scale.multiplyScalar(0.5); 
sphere.scale.multiplyScalar(0.5);
cylinder.scale.multiplyScalar(0.5);
Copy the code

It’s a little different from what you thought. My intention was to scale the group of objects as a whole, and not to deviate from each other. To fix this, I needed to recalculate the position of each object based on the scale of the other objects. But this is not a difficult problem to solve, and three.js provides an elegant way to deal with it. We can define an empty object, put three objects in it, and then apply the ratio to the parent object.

var pile = new THREE.Object3D();
pile.scale.multiplyScalar(0.5);
pile.add(box);
pile.add(sphere);
pile.add(cylinder);
scene.add(pile);
Copy the code

Now let’s do something a little more interesting.

I’m going to add rotation to this object combination, let’s try the cylinder that rotates around the surface of the sphere, as if it’s going to slide off.

It became something like this, and obviously, this is not what I wanted. We have two options here: first, mathematically work out the correct position of the cylinder relative to the sphere; Second, create another Object3D, put the cylinder and ball in it and rotate it. It sounds complicated and uncool.

So, we can try to compute the matrix ourselves.

First, I need to set the maxtrixAutoUpdate property to false, and then I can no longer modify the matrix through position, scale, and rotation.

box.matrixAutoUpdate = false;
sphere.matrixAutoUpdate = false;
cylinder.matrixAutoUpdate = false;
Copy the code

Now, I’ll use the applyMatrix method to solve this problem. To do this, we create a Matrix4 for each object, and then we multiply the matrix by that matrix to apply the subsequent operations.

var sphere_matrix = new THREE.Matrix4().makeTranslation(0.0.1.0.0.0); 
sphere.applyMatrix(sphere_matrix);
var cylinder_matrix = sphere_matrix.clone(); cylinder_matrix.multiply(new THREE.Matrix4().makeTranslation(0.0.0.75.0.0)); 
cylinder.applyMatrix(cylinder_matrix);
Copy the code

So these steps are going to unlock a lot of knowledge about what’s going on here.

First, we leave the box alone, because it doesn’t need to be moved.

Next, I created a translation matrix and applied it to the ball object.

Finally, on the cylinder, I clone the matrix information of the sphere and create a new translation matrix based on this, the cylinder will move 1.75.

Once you understand the steps above, you will know what to do in the last step.

All it takes is one line of code, applied to the ball:

Sphere_matrix. Multiply (new THREE. Matrix4 () makeRotationZ (0.25) - Math. PI *);Copy the code

It worked out the way you wanted it to. It was cool.

Methods used in the example

In the example above, I moved the ball and cylinder a certain distance along the Y-axis and used the makeTranslation method. What this method does is it creates a translation matrix. Next, I used the applyMatrix method. What this method does is it applies the translation matrix to the sphere and the cylinder.

So what is a translation matrix? How does it do a translation?

The most common 4×4 matrix in three. js is called transformation matrix, which represents transformation types including translation, rotation and scaling.

Use a simple math problem to illustrate the transformation matrix:

There is a starting point, which is represented by a vector which is Vector3(20,20,0); Now, I’m going to move it to another location, Vector3(30,60,0).

Next, I’m going to set up a translation matrix, which tells me how the vector moves.

t = |1 0 0 10|
    |0 1 0 40|
    |0 0 1 0 |
    |0 0 0 1 |
Copy the code

And then finally, we multiply our initial vector times our transformation matrix vector.

|20|   |1 0 0 10|   |30|
|20| x |0 1 0 40| = |60|
|0 |   |0 0 1 0 |   |0 |
|1 |   |0 0 0 1 |   |1 |
Copy the code

The transformation formula is as follows:

transformedVector = vector * transformationMatrix

My final transformation vector is equal to my original vector times my transformation matrix

Restore the formula using the method in our example above, namely:

var vector = new THREE.Vector3(20.20.0);
var matrix = new THREE.Matrix4();
matrix.makeTranslation(10.40.0);
vector.applyMatrix4(matrix);
Copy the code

In addition to panting, Three’s API also provides rotation and scale. MakeScale (x, y, z) is used to makeScale changes.

While rotation is more complicated, three.js provides the following rotation methods:

matrix.makeRotationX(angle);
matrix.makeRotationY(angle);
matrix.makeRotationZ(angle);
matrix.makeRotationAxis(axis, angle);
matrix.makeRotationFromEuler(euler);
matrix.makeRotationFromQuaternion(quaternion);
Copy the code

The first three methods represent rotation around the X, Y and Z axes respectively, which need no further elaboration.

The fourth method is an integrated version of the first THREE methods. The first parameter represents THREE.Vector3, which represents XYZ, and the second parameter is the radian of rotation. The following two lines are equivalent:

matrix.makeRotationX(Math.PI);
matrix.makeRotationAxis(new THREE.Vector3(1, 0, 0), Math.PI);
Copy the code

The fifth method represents rotation around the X, y, and Z axes, which is the most common way to represent rotation; The sixth method is an alternative way of representing rotation based on axes and angles.

Finally, the three.js API provides a way to create matrices that represent combinations of panning, rotation, and scaling.

var translation = new THREE.Vector3();
var rotation = new THREE.Quaternion();
var scale = new THREE.Vector3();
var matrix = new THREE.Matrix4();
matrix.compose(translation, rotation, scale);
Copy the code

Matrix multiplication

The point of matrix multiplication is superposition.

The figure above shows three changes: rotation, scaling, and movement.

By multiplying the three change matrices in order, a final change matrix can be obtained:

combinedMatrix = rotationMatrix * scaleMatrix * translationMatrix;
Copy the code

Three. Js provides two ways to multiply matrices:

  1. matrix.multiply(otherMatrix)
  2. matrix.multiplyMatrices(matrixA, matrixB)

The first way is to multiply a matrix by another matrix; The second method represents the result of setting the matrix to matrixA by matrixB.

We also use the first method in our example: multiply the matrix of the cylinder by the new translation matrix, and multiply the matrix of the sphere by a rotation matrix.

It should be noted that the commutative law of multiplication does not apply to matrix multiplication. Matrix multiplication has order, and the results of rotation and move are completely different from those of rotation and move.

The matrix of the inverse

In number operations, division is equivalent to the undo operation of multiplication:

4 x 5 = 20
20 / 5 = 4
Copy the code

But in matrix calculations, the same rule does not apply. We can’t divide a matrix by a vector, we can only undo it by multiplying a vector by the inverse of a matrix.

The changed vector = the original vector * the change matrix; Inverse = change matrix.inverse(); Original vector = changed vector * inverse matrix;Copy the code

The inverse matrix represents the opposite transformation.

Three. Js provides a method to calculate the inverse matrix:

var matrix = new THREE.Matrix4();
var inverseMatrix = new THREE.Matrix4();
matrix.getInverse(inverseMatrix);
Copy the code

In addition, the inverse matrix is also used when processing camera objects in 3D scenes.

The last

Matrices are a powerful tool in the 3D world, capable of representing any transformation as a similar structure and using the same calculation process. In fact, there is much more to the world of matrices than this, and I hope that this brief introduction will allow us to enter a deeper realm and handle more complex scenarios in graphics development with ease.