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.
The Camera
class tracks the following state:
eyeFromWorld
matrixThis 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.
The Camera
class provides several behaviors for initializing the camera, moving and turning it, and building the eyeFromWorld
matrix.
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
.
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.
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.
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.