Thanks to Brad Clark over at rigging dojo for pointing out the following, and another, and another posts regarding matrices and understanding them. The first two even have videos! They talk about a few issues I didn’t bother covering, so they’re definitely worth checking out.
In this post I wanted to cover the other main piece of matrix anatomy – scale. Now scale is tricky, as it doesn’t have a clear spot in the matrix. In fact if you’re dealing with scale things can potentially get murky.
Technically its impossible to extract scale and rotation from a general transformation matrix, but in reality its generally easy. What does this mean? Well basically a general transformation matrix can have shear as well as scale as well as rotation. And all three of these pieces of data live in the upper 3×3 quadrant of the matrix. But if you know you’re just working with rotations, translations and scale (which in my professional life has always been the case – but perhaps others have different stories?) then its possible and in fact pretty easy.
Now a scale matrix by itself is simple. Its a diagonal matrix with the sx, sy and sz values along the diagonal. Ie a scale of 1.5, 1.1, 2.5 would look like this:
| 1.5, 0, 0 | | 0, 1.1, 0 | | 0, 0, 2.5 |
As you might have guessed, a diagonal matrix has all zeros except for its diagonal – just like the identity matrix. Just a quick aside – the identity matrix is basically the matrix equivalent of the number 1. Just as any number multiplied by 1 equals the original number – the same goes for the identity matrix. Any matrix multiplied by the identity matrix equals the original matrix.
But the above is just scale matrix all by itself. To get the actual transformation matrix you have to multiply it with the rotation matrix. Lets do this now – we’ll call the scale matrix S and if we use the rotation matrix from the previous post and inventively call it R. So our scale times our rotation transformation matrix is S*R. Doing this gives us this:
| 1.5, 0, 0 | | 0, 0,-1 | | 0, 0,-1.5 | | 0, 1.1, 0 | * | 0, 1, 0 | = | 0, 1.1, 0 | | 0, 0, 2.5 | | 1, 0, 0 | | 2.5, 0, 0 |
So a couple of things – notice we multiplied the scale by the rotation – ie: the scale comes first in the equation. How do we know to do this? Well, in the case of maya we look at the docs for MTransformationMatrix – and I think its also stated in the docs for the xform mel command. These docs tell us how maya orders its transformations. I’m not sure if this is the general case for 3d applications – but I expect it is. Its pretty easy to test if you can’t find documentation.
Obviously if you have a more interesting rotation matrix, the result of multiplying it with the scale matrix can be quite messy, and the values get intertwined. This is what the matrix for a rotation of 35, 24, 18 in XYZ and the above scale looks like:
| 1.5, 0, 0 | | 0.87,-0.03, 0.50 | | 1.3, -0.05, 0.74 | | 0, 1.1, 0 | * | 0.28, 0.85,-0.44 | = | 0.31, 0.94,-0.49 | | 0, 0, 2.5 | |-0.41, 0.52, 0.75 | | -1.0, 1.31, 1.87 |
As you can see – this is a lot messier and harder to understand than the above example.
Factoring out scale from a transformation matrix is called matrix decomposition. Basically decomposition will take a single matrix and return a bunch of other matrices which when multiplied together will give you the matrix you had in the first place. So if you had a matrix that had translation, rotation and scale, if you wanted to halve the RX channel, you’d need to decompose the matrix, perform the operation on the rotation matrix, and then put it all back together.
How to do this? Well, its actually pretty easy, but we’ll need to understand a few more things first. First you need to know what a matrix inverse is. I won’t go into how to find a matrix inverse here – again there is a bunch of solid resources online to learn how this is done (and my vectors.Matrix class has an inverse method) - but I will go into what an inverse is and why its useful.
Basically a matrix inverse “un-does” the results of a matrix. So just say you have a transform T with matrix M. If we want to “reset” the transform of T, we can apply the inverse of M (called M-1) to the transform and the transform should be back at the origin (ie it’s local matrix will be the identity matrix).
Another way of saying this is that:
M * M-1 = I
Ie a matrix M multiplied by its inverse M-1 equals the identity matrix.
So back to decomposing scale from a transformation matrix. So remember from the first post that the first three rows of the matrix are the local basis vectors. Well, in a non-scaled transformation matrix the length of those vectors equals 1. When you scale a transform, those basis vectors get scaled. So if you have a transformation matrix scaled by 2 in X, then the X basis vector has a length of 2. So now it becomes really easy to extract the scale matrix from our transformation matrix – we just have to do this:
scaleX = Vector( M ).length() #M is row zero of the matrix M using the matrix class above scaleY = Vector( M ).length() scaleZ = Vector( M ).length() S = Matrix( [scaleX,0,0, 0,scaleY,0, 0,0,scaleZ], 3 )
This code will build the scale matrix for us. Now we know from before that the transform matrix is equal to the scale matrix multiplied by the rotation matrix – or to put it another way:
M = S * R
Using a bit of matrix algebra we get this:
S-1 * M = R
And so thats how we decompose the scale matrix from the rotation matrix. We can now ask the rotation matrix what its euler angles are or whatever else we need. The code for this would look like this:
scaleX = Vector( M ).length() scaleY = Vector( M ).length() scaleZ = Vector( M ).length() S = Matrix( [scaleX,0,0, 0,scaleY,0, 0,0,scaleZ], 3 ) R = S.inverse() * M print R.ToEulerXYZ()
Kinda neat huh?
Anyway, this post is getting kinda long so I might leave it there for now. I think in the next post I’ll talk a bit about matrix arithmetic quickly – because thats kinda important to understand. Matrix algebra is similar to normal algebra, but there are a few very important differences that you need to understand. Then the next topic will be using matrices to transform between difference spaces – which may or may not be part of the next post. We’ll see.
So I hope this was as clear as the first one. I know this stuff sounds kinda technical, but give it a go. Its easier than it sounds. And again, please leave feedback in the comments if you have any, its really appreciated. Also spread the word to those you think might benefit from having a better grasp on matrix math. Till next time!
This is post 2 in the matrix series.
This post is public domain
This entry was posted on Thursday, September 23rd, 2010 at 19:04 and is filed under main, tutorials. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.