In this lesson, we look at how to create and apply 3D transformation matrices to rotate and perform other transformations on 3D shapes created in WebgL.
We've seen how to go animation by changing the coordinates of individual vertices over time, but in general this is not how webgl animation is done. More usually you'll be building up 2 or 3D forms and transforming those forms, scaling, translating, or rotating them over time to animate them. These three types of transformations are done with 3D transformation matrices. I don't want to take a deep dive into matrix math here, but let's hit the basics.
A matrix is a grid of values represented within a pair of square brackets like so. With a 4x4 matrix like this you can perform any type of 3D transformation by plugging in various values in specific locations. You can then apply that transformation by multiplying that matrix in a vertex. The vertex will also be represented by a matrix like so. The second matrix in matrix multiplication has to have the same number of rows as the first matrix has columns. That's why we had that fourth W element to our 3D vertices.
Here's some common matrices for rotating on each 3D axis, as well as for scaling and rotating on any axis. If you want to do two operations at the same time, such as rotating on two axes, or rotating and translating for example, you either have to construct a more complex matrix, or more commonly create on matrix for each operation, and then multiply those matrices together, and then multiply that by the vertex matrix to get the final coordinates of the transformed point.
For this lesson we'll stick to single transforms. Now in webgl matrices are represented as arrays, but there's an important caveat when constructing these arrays. Say we had a matrix like this. The obvious first attempt would be to write it like so, looks good, right? Wrong.
This is what's known as row major order, because we're defining each row, then the next row, and then the next row, and so on. But webgl wants its matrix arrays defined in column major order, we list each column, then the next column, and so on. So while our array for that matrix would actually look like this. For example, if we take the rotation-Z matrix, that looks like this. When we turn that into a matrix array it looks like this. Interestingly, the only thing that's switched in this case is the sign and minus sign, but it's important.
More important when you get into some of the other matrices which will be very different when flipped. OK, let's code. I'm using the code from an earlier lesson that just draws a triangle on the screen. I'll make a function called rotateZ that will take a single parameter, angle. This will calculate the sign and cosign of that angle, and fill in an array just like we discussed. Now remember that to pass this data to webgl it has to be in a float32 array, so I'll just wrap that in one of those right here.
We need to pass this matrix to our vertex shader, so that it can transform all the vertices. So we'll jump over there. We'll make this a uniform, because every vertex is going to be transformed by the same matrix. The type of is math4, because it's a 4x4 matrix. We'll call it transformMatrix. Now as I said, we need to multiple this matrix by each vertex to get the final transform position. So we say gl-position = transformMatrix * coords. Glsl knows how to do matrix math, so you don't have to worry about that.
Also note that theorder of multiplication for matrix multiplication is very important, so it needs to be in the order I've done it here. That's it for the shader. Now back in our rotateZ function we need to get this uniform location. So we say glGetUniformLocation, passing in the shader program in the name of the uniform transformMatrix. Then we can simply assign our float32 array to this uniform with glUniformMatrix4fv(transformMatrix ,false, matrix).
The V in the name there indicates that we're passing a vector, or an array containing multiple values not 16 separate values. Notice that there is a new second Boolean value in there. Remember all that talk about column major versus row major? This variable asks if we want to transpose the matrix to swap it from being column to row major, which means that we could have written our array out by rows and set this to true to swap them.
The thing is though, it doesn't do anything in webgl, and actually is required to be set to false. It exists here just so this method has the same interface as openGL, which would allow you to do this. Sorry. Now just need to utilize this new function. We'll need an angle to rotate by, so I'll set that up at the top and make it zero. In the draw function I'll just call rotateZ, passing in the angle, and incrementing it by 0.01. I'll add in a call to request animation frames so we have an animation. Run it, and behold, motion.
That was fun, let's do another one. I'll just copy and paste the rotateZ function and change it to rotateY. This does the exact same thing as rotateZ but the matrix it creates is a little bit different, and I'll substitute rotateY for rotateZ up in the draw function. Now this doesn't look too impressive because we haven't set up perspective, and lighting, and we're just rotating a flat shape, but we'll fix all that in future lessons.
For now, play around with this. See if you can get a rotateX function there as well, and functions for scaling and translating. Google search for 3D matrix transformations will give you all the data you need. Try building up some more 3D-like shapes with extra vertices. We're on the edge of doing some really cool stuff her.