In computer graphics and computer games, matrices show up a lot. MonoGame will do most of the hard stuff for us, so it shouldn't be a big deal. I've created this tutorial to teach you everything you will need to understand about matrices so that you can get started with your game. Obviously, there is quite a bit more to matrices than what will be presented here, but this should be more than enough for now.
In this tutorial we will first look at what a matrix is, and then look at the three most common types of matrices in game development, and how to use them in your game.
What is a Matrix?
A matrix is simply a two dimensional grid of numbers, like the one below:
This matrix has four rows and four columns, but a matrix can have any number of rows or columns. And, of course, the numbers in a matrix can have any value in them, not just 1's and 0's. More than one matrix are called matrices.
You can do lots of things with matrices. They can be added, subtracted, and multiplied, like numbers. They also have other operations that you can do with them, aside from these simple ones. We don't need to look at the details too much, but if you want to know more about them, you can look up 'linear algebra' on Google or Wikipedia. There's a lot of information about them.
What we really care about is how matrices can be used to make cool games. For the purposes of our games, a matrix is just a mathematical toy that takes points in one coordinate space and transforms them into another coordinate space. For example, let's say we have a model that we've created in a 3D modeling program. When we create the model, all of the vertices are centered around the origin of the center of the model. However, in our game, we want to draw the model in a particular spot in our world, not at the origin. A matrix can be used to transform the model's coordinates into coordinates in our game world.
In our games, we can think of matrices as a type of transformation. We will see that matrices can do all sorts of things. And the best part is that XNA is built in such a way that we don’t even have to really know how it is doing it. Let’s now take a look at several matrices that are used throughout computer graphics.
World, View, and Projection Matrices
When we go to draw a model on the screen, there are typically three different transformations that need to be done. There are three different matrices that correspond to these three transformations. The standard transformation path looks like the image below:
We start off with our vertices in model space. The coordinates of our vertices represent where they are in relationship with the rest of the model. At this point, we apply the world matrix. This transforms our model space coordinates into world space. World space coordinates represent where the vertices (and the entire model) are in relationship to the whole world. The world matrix basically tells us where the model is located in the world.
The next step is to take our world space coordinates and apply another transformation to them. This next transformation is the view matrix. This matrix will put our coordinates into view space, which is where the vertices are in relationship to the viewer. That is, where the player's camera or eye is located at. In a sense, this transformation tells us where the player is located in the world.
The third and final step of the process is to apply the projection matrix. This matrix essentially tells us what type of camera we are using. It gives the computer the information it needs to determine where on the screen each of our vertices should appear. (It "projects" the points onto the screen.) This transformation essentially gets the vertices into screen coordinates, although the computer still has more work to do as it draws the model on the screen (like rasterization).
Making Matrices in MonoGame
We've now looked at how matrices are used, and how they are used in the fundamental transformation process of our game. We will now go ahead and see how we can create matrices in MonoGame. Like most other things, the MonoGame people have made this pretty easy for us.
World matrices can be the trickiest matrices to make, but just to be consistent with ordering, let’s discuss them first. There are three basic types of world matrices that we would want to use. First, we might want to translate (slide or move) the points of a model from one location to another. Second, we might want to rotate the points of a model. Third, we might want to scale the points of a model to make it bigger or smaller. These can all be done as follows:
To create a translation matrix:
To create a matrix that rotates around the x-axis:
(Don’t forget that you can use the MathHelper.ToRadians(float degrees) to convert from degrees if you want.)
To create a matrix that rotates around the y-axis:
To create a matrix that rotates around the z-axis:
Or to create a matrix that rotates points around an arbitrary axis:
Matrix.CreateFromAxisAngle(Vector3 axis, float angleInRadians);
Or to create a matrix from yaw, pitch, and roll (which will be discussed in a future tutorial):
Matrix.CreateFromYawPitchRoll(float yaw, float pitch, float roll);
Multiple World Transformations
The important thing to keep in mind with world matrices (all matrices in general, but more commonly with world matrices) is that you can also combine any the matrices above to make a combination matrix that performs both. For example, you could do the following to make a matrix that will rotate a model and then move it, as shown in the code below:
Matrix result = Matrix.CreateRotationX(MathHelper.ToRadians(45)) * Matrix.CreateTranslation(new Vector3(10, 0, 0));
It is important to do these multiplications in the correct order. In normal math, 3 * 2 is the same as 2 * 3. Order doesn't matter. However, with matrices, the order matters. The order reflects the order we perform our operations in. As an illustration, imagine you are standing at a particular point facing north, and you are going to move ten feet forward, and also turn right 90°. If you move forward then turn right 90°, then you will be ten feet north of where you were, facing east. If you turn first, then move, you will be ten feet east of where you were, facing east. Getting the order wrong makes very weird things happen. Make sure you get your matrices in the correct order. And the correct order is in the reverse order of the order you want them done. The example before will rotate first, and then after that, translate.
View matrices are much easier to work with than world matrices. In fact, there’s really only one method that you need to know. You can create a view matrix with the following line:
Matrix.CreateLookAt(Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector);
cameraPosition is the location of the camera in your 3D world, cameraTarget is the point in your world that the camera is looking at, and cameraUpVector is the direction that is ‘up’ for the camera. The up vector needs to be specified, because even though the camera has a location in space, and is looking at a particular point, the camera could still rotate around. You will only really deal with the up vector if you get into a sophisticated flight simulator or something like that. For the most part, you could use Vector3.Up (along the y-axis) for this value, and not worry about it beyond that.
You also want to make sure that the up vector is not in the exact same direction as the direction the camera is looking, or nothing will be drawn.
Projection matrices are not that difficult to work with either. There are several built in choices for projection matrices, depending on what you want the projection to look like. The two most common projections are orthographic projections and perspective projections. The main difference is that perspective projections have a vanishing point and orthographic projections do not. This means that distant objects in perspective projections will look small, while in orthographic projections, they will look the same size.
The images below show the differences between these two projections. The first image is a perspective projection, and was created in Wings3D.
This second image shows the same scene with an orthographic projection. This image was created in Wings3D as well.
Perspective projections tend to look more realistic, because it gives the feeling of depth. However, there can be times that orthographic projections are better. Many of the older real time strategy games used orthographic projections. If things are all relatively equidistant from the camera, then they are roughly equivalent in appearance, and using an orthographic projection may make it easier to do certain things, both for the user and for the programmers.
An orthographic projection can be created with the following code:
Matrix.CreateOrthographic(float width, float height, float zNearPlane, float zFarPlane);
With this, the width is the number of units across the projection should be, height is how tall the projection should be in units, and the zNearPlane and zFarPlane are the near and far clipping planes.
You can also create an off-center orthogonal projection (for example, the left side goes out farther than the right side). The following method creates an off-center orthogonal projection:
Matrix.CreateOrthographicOffCenter(float left, float right, float bottom, float top, float zNearPlane, float zFarPlane);
In this case, you need to specify how far out all four directions are (left, right, bottom, and top) and the zNearPlane is the near clipping plane and zFarPlane is the far clipping plane. It will probably come as no surprise to you that this method could also be used to create any ‘centered’ orthographic projection. For example, the following two lines are equivalent:
Matrix.CreateOrthographicOffCenter(-1, 1, -1, 1, 0.1f, 100f); Matrix.CreateOrthographic(2, 2, 0.1f, 100f);
There are three methods for creating perspective projections. The first one that we will discuss is the one that will be used in many of the other tutorials.
Matrix.CreatePerspectiveFieldOfView(float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance);
The first value is the field of view angle. The field of view angle represents the angle that the camera can see. The diagram below shows two different field of view angles.
Although these numbers are measured in degrees, the parameter in the CreatePerspectiveFieldOfView() method needs to be in radians, so you may have to convert from degrees to radians. The aspectRatio parameter is the aspect ratio of the window. That is, it is the ratio of how long the window is to how tall the window is. An aspect ratio of 1.0 means that it is as tall as it is wide. An aspect ratio of 2.0 means that the window is two times as wide as it is tall. An aspect ratio of 0.5 means that it is half as wide as it is tall. Many times, it will be useful to just get the window size from the system, rather than guessing, or trying to remember what it was set to. This can be done with code similar to the following:
float aspectRatio = graphics.PreferredBackBufferWidth / graphics.PreferredBackBufferHeight;
Finally, the nearPlaneDistance and farPlaneDistance are the near and far clipping planes that we have discussed before.
A second method for creating a perspective projection is found below:
Matrix.CreatePerspective(float width, float height, float nearPlaneDistance, float farPlaneDistance);
In this method, the width is the width of the viewing area at the near clipping plane. The height is the height of the viewing area at the near clipping plane. The nearPlaneDistance and farPlaneDistance are once again the near and far clipping plane distances. Similar to the off-center orthographic projection, there is an off-center perspective projection, as shown below:
Matrix.CreatePerspectiveOffCenter(float left, float right, float bottom, float top, float nearPlaneDistance, float farPlaneDistance);
Like with the off-center orthographic projection, you need to specify how far out the window extends to the left, right, bottom, and top. These values are, once again, the values at the near clipping plane. The nearPlaneDistance and farPlaneDistance values are the near and far clipping plane distances.
While we have discussed numerous ways to create matrices, what if the matrix we need doesn't have a method for creating it? As the last topic in this tutorial, let’s take a look at how to create a custom matrix. The Matrix constructor can be used to create a matrix with given values, as shown below:
Matrix customMatrix = new Matrix( float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, float m31, float m32, float m33, float m34, float m41, float m42, float m43, float m44);
This will let you assign anything you want to a matrix. In addition to this constructor, you can modify the values of an existing matrix by using the set of properties that belong to the matrix. For example, you could use the following line to modify the element in the third row and first column of a matrix:
customMatrix.M31 = 4;
You will probably find that almost every matrix you will need can either be created with the default factory methods described earlier, or a combination of more than one of them. But these methods give you the option to make any changes your imagination can dream up a use for.
A Final Example
This tutorial is probably already long enough, but I thought that it would be good to put one last example in, showing some code that prepares all three of the standard matrices:
Vector3 cameraPosition = new Vector3(30.0f, 30.0f, 30.0f); Vector3 cameraTarget = new Vector3(0.0f, 0.0f, 0.0f); // Look back at the origin float fovAngle = MathHelper.ToRadians(45); // convert 45 degrees to radians float aspectRatio = graphics.PreferredBackBufferWidth / graphics.PreferredBackBufferHeight; float near = 0.01f; // the near clipping plane distance float far = 100f; // the far clipping plane distance Matrix world = Matrix.CreateTranslation(10.0f, 0.0f, 10.0f); Matrix view = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up); Matrix projection = Matrix.CreatePerspectiveFieldOfView(fovAngle, aspectRatio, near, far);
Well, that was probably quite a bit of information, but hopefully, it helped you to understand what a matrix is and how it is used in games. Like I said before, there is quite a bit more to matrices than this, but this should be enough to get us going.
|Having problems with this tutorial? Try the troubleshooting page!|