Matrix Basics. How to step away from storing an orientation as ’3 angles’
While participating in the XNA forums over the last several years, I often see folks posting questions about how to get their rotations the way they want them. Most of the time they post a code snippet and it shows they are trying to store and manipulate their model’s orientation using ’3 angles’, typically stored in a Vector3 for convenience. Usually there is much frustration involved. The ’3 angles’ approach does not lend itself to concatenating (combining) rotations. It is also complex to resolve a rotation around a non-aligned axis in terms of getting the order and proportion correct.
I’ll go out on a limb here and say that eventually, anyone who continues on in XNA (or 3d programming in general) past the rock bottom beginner level, migrates away from this ’3 angles’ approach. The sooner you give up the ’3 angle’ approach (AKA Euler Angle Representation), the sooner (I believe) you will be effective as a 3d programmer. Although the ’3 angles’ representation is a legitimate way of expressing an orientation and I guess there is a time & place for everything, I haven’t found a serious project that uses this method. Maybe there is some out there, but I’ll bet they are rare and unusual enough to deem them not exemplar of game programming anything more complex than beginner projects.
I’ll have to admit the ’3 angles’ approach is initially intuative and possibly some beginner level tutorials use this approach for that reason. I have an opinion that that is a dis-service to the student since it seems lure them into the realm of 3d programming but in short order directs them right into a brick wall… But what good is expressing an opinion without offering also some possibility of an alternative. So this article is an attempt at offering an alternative. And, happily, no math will be required.
Amongst the other approaches that are out there to represent an orientation, in this article I am going to push the Matrix. I think others are good too but learning about and using a matrix will facilitate 3d programming knowledge and get you ready to explore other approaches without turning you into any brick walls. Additionally, since you must ultimately pass the model’s orientation onto the shader in matrix form anyway, there is an added minor benefit in not having to convert to a matrix before doing so.
As intuitive as the ’3 angle’ approach is, the Matrix approach might be that daunting to the beginner. But it doesn’t have to be. I believe that after learning the basics of a matrix and how to visualize a matrix in the mind’s eye while manipulating it, it will be very easy for the beginner to pick up. So for the cost of investing a little time to understand the Matrix, your effectiveness in 3d programming will increase dramatically and you can stop the insanity of banging your head against the ’3 angles’ brick wall.
So what is a matrix, and how do I go from my understanding of ’3 angles’ to an understanding and implementation of matrices? I believe the key is in the ability to visualize a matrix as you are coding changes to it. OK, let’s start by showing a sample demo code that utilizes a matrix approach instead of a ’3 angles’ approach. All the supporting code necessary to render an object has been stripped away just so you could see how the matrix code fits in to a game class.
Class Game1 : Game
//Declare a matrix with sufficient scope to access it in the Update & Draw methods
modelOrientation = Matrix.Identity; // initialize the matrix to a starting point
modelOrientation *= Matrix.CreateScale(?);//This is the place to apply scale if necessary
//manipulate the matrix
modelOrientation *= Matrix.CreateFromAxisAngle( someVector, someAngle);
//manipulate the matrix a second time if necessary
modelOrientation *= Matrix.CreateFromAxisAngle(differentVector, differentAngle);
//manipulate the matrix yet a third time, or as many times as you want
modelOrientation *= Matrix.CreateTranslation(yetAnotherVector);
effect.World = modelOrientation; // pass the matrix onto the effect for drawing
So if you implemented this, a model would be rotating and moving depending on what values you place for ‘someVector’, ‘someAngle’, …etc.
A very important thing to understand from the above example is the ease of how movements or rotations can be concatenated to each other (combined to each other). When working with matrices, you can break your movements/rotations down into a series of understandable operations and simply perform them one after another. That was one source of problems with the ’3 angles’ approach, It was very very difficult to do the following: ” first I want to rotate it a little about this world axis, then a little around that world axis, then that 3rd axis, then that other world axis, etc… The ’3 angles’ representation does not support that because after the first rotation, one of the subsequent rotations could produce an undesirable result, but the matrix representation does not have that problem. There is no limit to how many rotations you can combine and all rotation axis will work out just like you expect.
One thing to notice is how interchangeable the word “Matrix” is with the word “Model” during this discussion. If we say ‘I am rotating the matrix some angle about some axis’, we may as well say ‘I am rotating the model some angle about some axis’. The end result is identical and the same meaning is conveyed.
I know after seeing the code above you may want to jump right into examples of moving and rotating, like figuring out how to determine what ‘someVector’ and ‘someOtherVector’ should be. But first I want to go over the basics of what the Matrix is and why it works and what it can do for you. Most importantly, I believe it will be most helpful for you to learn how to visualize a matrix in your mind’s eye. I keep repeating that, don’t I? I must think it’s important.
A Matrix in XNA is made up of 16 numbers, and mathematically, they fit into a 4 row by 4 column structure. Here is a matrix in textual form. It happens to be an Identity matrix:
1 0 0 0 row 1
0 1 0 0 row 2
0 0 1 0 row 3
0 0 0 1 row 4
So the first way you should think of a matrix is simply a group of four vectors. Each row is a like a Vector4 but you can disregard the 4th element of each row for the moment. They’re one of those things that when you get to the point that you need them, you will understand them. The first 3 elements of each row are all you need to understand to manipulate models in 3d space. So think of the first 3 columns as holding the X, Y, & Z components of each row’s vector. The first 3 rows of the matrix are what sets the model’s orientation/rotation and the 4th row is the one that sets the position of the model.
An identity matrix is a matrix that applies an orientation to the model that has a specific alignment with the world axis (the X, Y, Z). It also places it’s position at the world origin. Generally, it should position and orient the model just as it was in your 3d modeling app. The textual matrix above is an Identity matrix. Here is a graphic of an Identity matrix.
In this graphic, each of the rows becomes a vector. The upper three vectors(rows) are depicted as the colored arrows. Whenever you visualize working with matrices, always try to visualize these three vectors. Not in terms of their X, Y, Z values but in terms of what direction these three vectors are pointing. On the 1st row, for example, the st value is 1 so that vector’s X component is 1. The 2nd & 3rd values are 0, so the Y & Z components of that vector are 0. that makes a vector3 like this: (1, 0, 0) which point straight down the x axis just like the red vector/arrow in the graphic. One of the nice things about working with matrices is that you rarely need to know the actual numerical values of any of the 16 numbers.
Now, there are a couple things to learn about these vectors. When we use a matrix to represent an orientation or rotation, we always want these three vectors to be 90 degrees to each other. They will not always be aligned with the world axis like they are in this graphic, but they must always be 90 degrees to each other. Also, notice they all have a length of 1. This is important, if one of the three vectors had a length of, say, 2, the model would stretch in that direction when drawn. If all 3 vectors had a length of 2, the model would scale to be twice as long, wide, & tall as the original. That is actually the basis of a scaling transform. But for now, let’s keep visualizing the matrices we work with here as having a length of 1. When the 3 vectors are 90 degrees to each other, they are said to be orthogonal. When a vector has a length of 1, it is said to be normalized. When a matrix’s vectors meet these two criteria, the matrix is said to be orthonormal. If your matrix is not orthonormal, your model will be either skewed, distorted, or scaled. This may or may not be desirable depending on your project.
Another thing to notice on the graphic above is the way all three vectors meet at the world origin. Technically speaking, a vector always emanates from the world origin no matter what so for the life of the matrix, The these three vectors will stay put and simply rotate together around this origin. Here is a short video showing these three vectors rotating together.
Notice all three vectors rotate as if they were one assembly and always stay 90 degrees to each other.
So let’s introduce the 4th row’s vector into this mix. The 4th vector represents the position the model occupies in the game world. Say we added a line of code to add an arbitrary position to this Matrix.
modelOrientation *= Matrix.CreateTranslation(new Vector3(1.25f, 1.25f, -1.0f));
Here is a graphic to represent this:
If we were to draw a model using this matrix, the model would be drawn at the end of the translation vector; like this.
So just remember that the model is always drawn at the end of the translation vector (the 4th row vector). Let’s remove the model for a few moments and show some rotations. In the next video, we are going to place an arbitrary rotation axis vector to use to rotate the matrix around.
Vector3 someArbitraryRotationAxis = new Vector3(-0.8f, 0.8f, -0.4f);
float someArbitraryAngle = MathHelper.ToRadians(83);
modelMatrix *= Matrix.CreateFromAxisAngle(someArbitraryRotationAxis, someArbitraryAngle);
So in this video, the vector ‘someArbitraryRotationAxis’ is depicted by a yellow vector and you will see that ALL 4 vectors of modelMatrix rotate around that axis.
So the rotation angle was about 83 degrees around that arbitrary axis. Aren’t you happy you were able to do that without having to calculate how much should have been around the global X, global Y, & global Z axis and what order to perform the three rotations?? Good… The reason I emphasized the word ‘all 4′ is because often times we want an object to rotate in place rather than orbit the origin and we forget that the position of the model will be affected by rotations.Wanting a model to rotate in place would be like rotating the three Rotation vectors (the ones making the tripod) while keeping the translation vector (the 4th row vector) stationary. That won’t happen though… all 4 vectors rotate when you apply a rotation to a matrix. But don’t worry, there is a way to make a model rotate in place. We’ll get to how to do that soon. But for now, just know that when a matrix rotates, all 4 vectors rotate and since the translation vector is one of them and is going to rotate, the model will be displaced.
Now I am going to throw you a curve ball. I’m going to do this in the name of ‘improved visualization’ technique. We’re going to move the 3 rotation vectors from the world origin to the end of the translation vector. Like this
This is not technically a correct depiction of reality because all vectors must emanate from the world origin. They can not be just sitting ‘out there’. But as long as we know that and account for that when needed, this viewing style can improve our ability to visualize what we need to do with the matrix while working with it. Consider these two pictures
Both of these pictures have the exact rotation applied to the 4 vectors. They are identical matrices. The only difference is how we are viewing them. The upper one is technically correct but the lower one is more visually helpful. Now, knowing that an aircraft pitches around it’s own lateral axis, which picture makes it more obvious that to pitch the nose of the aircraft up, we would need to rotate it around the matrix’s red vector. Hopefully, you would agree that the lower one makes it more obvious. I always tend to visualize a matrix as my model with the three vectors sticking out of it.
In the next video, I include both view styles to demonstrate that either style produces the same relationship between the model and the 3 rotation vectors. Check this out & pause it at different places to note that the 3 vectors at the origin are always parallel to the 3 vectors at the model.
So you can decide which of those two viewing styles you prefer when you are visualizing your matrices. A purest may want to keep the 3 rotation vectors at the origin, but a practical person may want to think of them as being at the model. Who knows, maybe there are times when you might find it convenient to go either way. No problem there. But for the remainder of this article, we’ll choose the style that places the vectors at the model.
Moving on from that point, there is another thing I want to reiterate from that video. It is important to know that the three vectors never move or rotate relative to the model. The three vectors are always pointing out the top/right/back of the model no matter how the matrix/model rotates. It is as if they are glued together. This is one of the reasons that matrices make it so easy to work with when doing rotations. Often, the rotation desired is one that pitches, rolls, or yaws (or a combination of) around these vectors. To pitch this aircraft up the rotation method would be as simple as:
modelMatrix *= Matrix.CreateFromAxisAngle(modelMatrix.Right, someAngle);
That’s it, absolutely no need to calculate any angles about global axis. This method simply takes that red vector, and rotates the whole matrix around it appropriately. I hope you are starting to see the ease of working with matrices instead of the ’3 angles’ approach.
So that introduces a new term; ‘modelMatrix.Right‘. What’s this ‘Right‘ thing? The XNA Matrix has seven properties which are these vectors we’ve been talking about (well, more than just the 4) and are readily available to you at any time. Here is a graphic showing these properties. Notice that the red vector, which is a matrix’s 1st row vector, is called Right. The green vector ( the matrix’s 2nd row), is called Up. The third row vector, the blue one, is called Backward.
The Forward, Left, & Down vectors are simply opposites of the three vectors that make up the matrix. When you access the Forward property, for example, the XNA framework will go to your matrix, grab the Backward Vector, Negate it, and make that the result of the Forward‘s getter. The 7th property is the Translation vector with, as you know by now, represent the world space location of your model.
Every time you apply a rotation or translation to a matrix, it automatically calculates and updates all the changed components that make up these 7 vectors (the X, Y, & Z components). It does this every time a rotation or translation method is called in every frame. This allows you accurate access to any of these vectors at any time. For instance, Let’s say the matrix has an arbitrary orientation to it, you could do a test like this:
If( modelMatrix.Forward.Y > 0.0f )
//the model is pointing somewhat upwards (inclined)
Now let’s bring the aircraft back in & touch on translating it. Hopefully, you should be able to imagine your model with the six vectors sticking out of it and know that your translation vector is going from the world origin to the model. So if you were told, say, picture in your mind’s eye a model and it’s matrix in the world at some slight rotation, you should be able to see something like the following graphic in your mind’s eye:
So since this is an aircraft and aircraft generally go forward, let’s talk about how to move this matrix/model forward without rotating it. For now, we’ll just use that Matrix.Forward vector as our direction. We’ll choose a speed to go, and create a velocity vector to move our matrix.
float speed = 0.15f; // or whatever value suits your game
Vector3 velocity = modelMatrix.Forward * speed;
modelMatrix *= Matrix.CreateTranslation(velocity);
So if this was run, every frame the aircraft would go forward 0.15 units. That might look something like this. [Note, you must imagine the translation vector, my 3ds movie making skills are not sufficient to make it animate properly]
One last thing I want to go over before closing this article is the way to rotate a model in place. Remember that when you rotate a matrix, all 4 vectors rotate about whatever axis you are using (including the 4th row translation Vector) which can make the model orbit the axis. To rotate a model in place, you must separate the changes in position from the changes in rotation. To do that, you must zero out the translation vector before applying any rotations. Look at this code here:
//assume the matrix’s translation vector is representing a position some distance from the origin
Vector3 position = modelMatrix.Translation;
modelMatrix *= Matrix.CreateFromAxisAngle(someAxis, someAngle);
//although the previous line rotated the 4th vector, we don’t care because…
modelMatrix.Translation = position;//we simply reset it to what it was before the rotation
So if you are beginning to visualize what the matrix is doing as you read/write your code, we might not need a video to show what’s happening. As you read/write the above code, you would see it rotate a little bit around the appropriate axis, then because we noted the exact position that it was at when we started, we move only the translation vector back to where it was. It is now sitting in it’s same spot but rotated to some extent. Do this frame after frame after frame and you have a model spinning in place. So, if you don’t capture the Translation vector before rotating the matrix, then reset the translation vector after the rotation to what it was before, your model will orbit the axis instead of rotate in place.
So the main objective of this article has been to offer someone starting in XNA programming an introductory visualization technique to effectively work with matrices and to encourage that someone to store and represent your model’s orientation in matrix form. To be comfortable and effective with matrix representation, I feel that the ability to be able to realistically visualize what is happening as you contemplate your code is paramount. Hopefully, this article has assisted some folks down that path. Thanks for reading.