Libraries such as XNA, DirectX or OpenGL provides a set of facilities for rendering different types such as polygons and objects, but they normally do not include things you are used to in a 3D game engine. The facilities missing is normally something you put together yourself, often using built math facilities in the target library and platform.
This tutorial is meant to be very simplistic and will only show the basics of how you can build a reusable camera class for your own projects.
A camera is defined as a point in 3D space the camera is located and another point in which the camera is pointing towards, in addition the camera has a vector indicating what is up in the world. With these 3 variables we can manipulate the camera as we want to. By using these variables (and a couple of others) we can calculate the matrices needed to translate the 3D world onto the screen. We need 2 matrices; a view matrix that will rotate the world according to the camera in 3D and then we need a projection matrix that will translate the 3D data to the 2D screen.
In XNA we have a lot of math methods to help us achieve this very easy :
First we need to calculate the aspect ratio of the 2D screen, we will need this to create our projection matrix.
this._aspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
We take the viewports width and divide it by the height of the viewport, e.g. 640 / 480 = 1.33333...
Then we can calculate the projection matrix by using a static method on the Matrix class :
this._projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(40.0f),
this._aspectRatio,
1.0f,
10000.0f);
The first argument is the field of view in radians. By using the MathHelper.ToRadians() method we can convert from angles to radians. In our example we create a field of view of 40 degrees. Then we pass in the aspect ratio we calculated. The last two arguments represents the near clipping distance and the far clipping distance. These arguments are the distance from the camera when objects/polygons are clipped. The projection matrix will most likely not change, unless you rescale the viewport or want to change the field of view or the clipping distances, so it is sufficient to do this during construction of the camera.
We now have the projection matrix, we only need the view matrix in order to complete our camera.
this._viewMatrix =
Matrix.CreateLookAt(this._position, this._lookAt, Vector3.Up);
The Matrix class provides a static method called CreateLookAt(), this method takes in the position of the camera the location where the camera is looking at and a up vector which we get from the Vector3 class since we are pretty much going for default stuff. This matrix creation should be called every time you render your scene, in case the camera has changed.
All the code above assumes you have a set of properties and fields setup in your class, the entire class will look something like this :
public class Camera
{
private Vector3 _position;
private Vector3 _lookAt;
private Matrix _viewMatrix;
private Matrix _projectionMatrix;
private float _aspectRatio;
public Camera(Viewport viewport)
{
this._aspectRatio = ((float)viewport.Width) / ((float)viewport.Height);
this._projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
MathHelper.ToRadians(40.0f),
this._aspectRatio,
1.0f,
10000.0f);
}
public Vector3 Position
{
get { return this._position; }
set { this._position = value; }
}
public Vector3 LookAt
{
get { return this._lookAt; }
set { this._lookAt = value; }
}
public Matrix ViewMatrix
{
get { return this._viewMatrix; }
}
public Matrix ProjectionMatrix
{
get { return this._projectionMatrix; }
}
public void Update()
{
this._viewMatrix =
Matrix.CreateLookAt(this._position, this._lookAt, Vector3.Up);
}
}
During construction of your game you initialize your camera :
this._camera = new Camera(graphics.GraphicsDevice.Viewport);
this._camera.LookAt = new Vector3(0.0f, 0.0f, -1.0f);
In your Draw method you do something like this :
this._camera.Update();
...
...
When drawing your object(s), walk through the "effects" in the mesh and set the following :
effect.View = this._camera.ViewMatrix;
effect.Projection = this._camera.ProjectionMatrix;
Then you are good to go with a camera class that is easier to manipulate and have a more better feel to it than using the Matrix, MathHelper and Vector classes directly to achieve the same result. In addition you get an abstraction on top of XNA that will enable you in some ways to be able to create games that aren't aware of the platform they are running on.