BannerLeft BannerRightToymaker

 

XNA Camera

These notes describe one way of implementing a camera in XNA. You should read the notes on XNA Matrix before reading these notes so you are clear about the role of the view matrix in the 3D transformation pipeline.

These notes are made up of the following sections

The problem with just using CreateLookAt

Up to this point you have probably been using the Matrix.CreateLookAt function to create your view matrix. As described in the XNA Matrix notes this function takes Vector3 values defining the camera position, the look at point (sometimes called the point of interest) and which way is up. It returns a matrix that will convert your game geometry from world space into view space i.e. from the point of view of the viewer. It is a useful function and ultimately we will use it in these notes to generate the  final matrix however it is no good on its own in implementing a 3 degrees of freedom camera.

What we would really like to do is to be able to provide rotations around any axis and a world position and then create our view matrix.

Camera Space and Axis

Transformation of geometry from one space to another can be thought of as a change in co-ordinate system. Our model is initially defined in a 3D art package with 0,0,0 normally at the centre of the model. It is then transformed into world space where 0,0,0 is the centre of the world. The model will be offset into that world somewhere. We have changed co-ordinate system. The same goes for converting from world space to camera space where we specify the camera position and rotation in world space hence converting the geometry into camera space.

Any co-ordinate system has an axis. The camera method described here works by defining the camera space axis in world space and then rotating it to the correct camera orientation. The resulting axis along with the camera world position can then be used to generate the view matrix and can also be used to easily allow camera movement as we will see later e.g. move forward, move left etc. The camera axis start orientation:

CameraAxis

The Up, Look and Right vectors represent our camera space axis. To fully define our camera we will also need a camera position.

Each time we calculate our view matrix we start with the axis as shown above

  • Up pointing straight up y (0,1,0)
  • Look pointing into the world z (0,0,-1)
  • Right pointing to the right on x (1,0,0)

We then rotate the axis to the correct orientation for our camera. It is better to do it this way rather than by maintaining a view matrix and constantly altering it as that way small floating point errors can occur. These mount up until the camera axis collapses on itself and you lose the ability to rotate correctly.

Yaw, Pitch and Roll

As mentioned in the XNA Matrix notes rotations are often described as yaw, pitch and roll:

  • Yaw is rotation around the y axis e.g. to look left and right
    • To implement yaw we need to create a matrix that rotates around the camera's up vector (local y axis)
  • Pitch is rotation around the x axis e.g. to look up and down
    • To implement this we need to create a matrix that rotates around the camera's right vector (local x axis)
  • Roll is rotation around the z axis e.g. to lean left and right
    • To implement this we need to create a matrix that rotates around the camera's look vector (local z axis)

Note that dependant on the game we may not use roll much at all. If we are making a first person shooter for example then roll may never be used however if we were making a flight simulator it may be very important. Also bear in mind that you may wish to clamp the rotations so the user cannot do odd things like looking up so far they end up looking behind themselves :)

To create the rotation matrices we can use the provided XNA matrix function:

Matrix.CreateFromAxisAngle(axis, angle)

This allows you to pass a vector (in our case up, look or right) that represents a camera axis and also an angle (in radians). It returns a matrix capable of rotating  by the angle around the axis.

Transforming the Camera Axis

To code this we need to create the matrix and apply them to our camera axis system to rotate it to the correct orientation while maintaining its axis shape. I am assuming here that you are holding your three rotations in a Vector3 called rotations.

To apply Roll (Z rotation) what should we rotate up and right around the look vector:

Matrix rollMatrix=Matrix.CreateFromAxisAngle(look, rotations.Z);

up = Vector3.Transform(up, rollMatrix);
right = Vector3.Transform(right, rollMatrix);

To apply Yaw (Y rotation) we need to rotate the look and right vectors around the up vector:

Matrix yawMatrix=Matrix.CreateFromAxisAngle(up, rotations.Y);

look = Vector3.Transform(look, yawMatrix);
right = Vector3.Transform(right, yawMatrix);

To apply Pitch (X rotation) we rotate look and up around the right vector:

Matrix pitchMatrix=Matrix.CreateFromAxisAngle(right, rotations.X);

look = Vector3.Transform(look, pitchMatrix);
up = Vector3.Transform(up, pitchMatrix);

We now have the camera axis rotated correctly. As you will see in a minute the up, right and look vectors are very useful for handling movement as well as for creating our view matrix.

Note: all of the above calculations should be done in a CalculateViewMatrix function that is called regularly, either when the camera changes position / rotation or at the beginning of each frame. They need to be done in the above order and not outside of this function. To rotate and translate the camera we simply maintain a rotations and position vector that is used during the CalculateViewMatrix function. You can call the function at any point but don't split up the above code.

Creating the View Matrix

The CreateLookAt function requires a camera position, a target and an up direction. We know the camera position and the up vector but we do not know the target. However the look vector points in the direction the camera is facing so we can use that to create a target by adding it to the position.

Vector3 target = position + look;
ViewMatrix=Matrix.CreateLookAt(cameraPosition, target, up);

Handling Movement

Your camera axis system is now all set up to help with movement. Your up, look and right vectors are normalised so can just be multiplied by a speed value to allow movement in any axis direction.

Where position is the camera position vector as a Vector3 and speed is a floating point value:

// to move right
position+=right*speed;
// to move left
position-=right*speed;

// to move forward
position+=look*speed;
// to move back
position-=look*speed;

// to move up
position+=up*speed;
// to move down
position-=up*speed;

To rotate we just add or subtract from the appropriate rotations' member. The only thing to look out for is when a rotation goes passed 360 degrees (2 PI). For example to yaw right we need to increment Y, so a function to rotate around y could be:

// Y axis rotation - also known as Yaw
private void RotateAroundY(float angle)
{
    m_rotations.Y += angle;

    // keep the value in the range 0-360 (0 - 2 PI radians)
    if (m_rotations.Y > Math.PI * 2)
        m_rotations.Y -= MathHelper.Pi*2;
    else if (m_rotations.Y < 0)
        m_rotations.Y += MathHelper.Pi * 2;
}

Constructing your Camera Class

You will need to add a class to your game to represent the camera. Where does it go? Well the code to calculate the view matrix is very much XNA related and graphics related and so should go in your graphics component. However the position and rotation of the camera is world simulation data. Ultimately you may want more than one camera so a complete solution would probably involve the mechanisms of calculating the view matrix in the graphics component but having actual camera entities in the world component. Having said that for the majority of cases it would be fine to just have a camera class also containing world data in your graphics component. This is what I will describe below.

Fields and properties

Your camera class will need to have data for:

  • position - the camera position. Can be held in a Vector3
  • rotation - the camera rotation around each axis. Can also be held in a Vector3 for convenience as it has x, y and z members
  • up, right and look - the vectors defining the camera axis. Vector 3 types.
  • viewMatrix - a matrix calculated in the camera. Matrix type.

I would not allow any of these to be altered from outside the class. To allow external code to change the camera I would support it via calls like CalculateViewMatrix, MoveCamera and RotateCamera and one function for setting the start position of the camera. Generally you should not expose fields outside your classes. If you do want to provide read access to any of these then do it via a property and make the get public but the set private.

Functions

Your camera needs to be able to support calls to

  • Set the initial position and orientation of the camera
  • Rotate the camera in any of the axis with a provided angle
  • Move the camera in any axis with a provided speed value
  • Get the view matrix. The view matrix is required when drawing your geometry so needs to be available from outside the class.

Rather than write 3 functions to support translation and 3 to support rotation it is better just to provide one public function that takes an enum specifying which axis to use and then calls internal private functions to actually do the action.

public enum ECameraRotations
{
            eYawLeft,
            eYawRight,
            ePitchUp,
            ePitchDown,
            eRollClockwise,
            eRollAnticlockwise
};

Then have a function

public void Rotate(ECameraRotations rot, float angle)
{
    switch (rot)
    {
        case ECameraRotations.eYawLeft:
            RotateAroundY(angle);
        break;
        case ECameraRotations.eYawRight:
             RotateAroundY(-angle);
        break;
        case ECameraRotations.ePitchUp:
             RotateAroundX(angle);
        break;
// etc. for the others then :
     }
     CalculateViewMatrix();
}

Note that I calculate the view matrix whenever a change is made. This means it and the camera axis is always correct. CalculateViewMatrix will of course be private and will be implemented as described above.

You will also want an equivalent enumerator and function for moving the camera.

Summary

To implement a camera we need to create a view matrix from camera rotations and translation. The above notes work by altering a camera axis system based on rotations and then combining these values with position to calculate the view matrix. The advantages of this system are that we reduce floating point errors and we provide an axis system that allows easy movement of the camera in the world.

Please note that other ways of implementing a camera exist. A quick Google should find them. One thing to consider is using Quaternions which can provide smoother camera rotation.

Further Reading



© 2004-2009 Keith Ditchburn  (A lecturer on the Games Programming Course at the Teesside University)