Camera Class

In which you organize the data and methods related to managing a camera into a class.

You just read about the handy lookAt function that turns a camera's position, focal point, and an up vector into a matrix that transforms world space into eye space. This function is self-contained and could be placed as a static method in your Matrix4 class. However, if you are planning on making the camera move around the scene, you are better off creating a Camera class that encapsulates its state and behaviors.

State

The Camera class tracks the following state:

This is the state that you will need to access regularly. Other data, like the camera's up vector, is temporary and should be relegated to local variables.

Behaviors

The Camera class provides several behaviors for initializing the camera, moving and turning it, and building the eyeFromWorld matrix.

Constructor

The constructor receives its position, the position it is looking at, and the world's up vector. From the two positions, it computes the forward vector. Instead of constructing the look-at matrix directly, it defers to a helper method named reorient.

Reorient

The eyeFromWorld matrix is built in the reorient method, which assumes the camera's position, the forward vector, and the world up vector have already been set. It computes the camera's right and up vectors and multiplies the rotation and translation matrices together.

Strafe and Advance

Through the constructor you can place the camera anywhere in the world. Once the camera is positioned, you might want to move it to nearby locations. Three kinds of relative camera movement are commonly supported in immersive graphics: strafing to the left and right, advancing forward and backward, and elevating up or down.

You strafe the camera by pushing its position along the right vector and then rebuilding the camera's transformation matrix, as shown in this pseudocode:

function strafe(distance)
  from = from.add(right * distance)
  reorient camera

If the distance is negative, the camera moves left.

You advance the camera by pushing its position along the forward vector:

function advance(distance)
  from = from.add(forward * distance)
  reorient camera

If the distance is negative, the camera moves backward.

You elevate the camera by pushing its position along the world's up vector:

function elevate(distance)
  from = from.add(worldUp * distance)
  reorient camera

To implement a jump or crouch, you'll need to animate the elevation changes.

Rotation

Imagine you are piloting an airplane and a flight controller tells you to turn 30 degrees. This isn't a helpful command because you have a lot turns to choose from. You could turn left or right, which is called yawing. You could turn the nose up or down, which is called pitching. You could turn about the noise, which is called rolling. Or you could rotate around some other axis.

Cameras are like airplanes in that they turn in many directions. Users typically make them yaw and pitch in order to look at nearby objects in the scene. Rolling isn't as common. You don't roll much as a human, and even if you do, your superior and inferior oblique muscles rotate your eyes to compensate.

You yaw the camera by rotating the forward direction around the world's up vector, which is accomplished through a matrix-vector multiplication:

function yaw(degrees)
  forward = rotateAroundAxis(worldUp, degrees) * forward
  reorient camera

You pitch the camera by rotating the forward direction around the right vector:

function pitch(degrees)
  forward = rotateAroundAxis(right, degrees) * forward
  reorient camera

If the camera pitches up or down too much, the forward vector may end up aligning with the world's up vector. This will lead to some bad dot products and an unstable view. You may want to track how much pitch has been applied and constrain the total amount so it doesn't reach -90 or 90 degrees.