First-person Camera
The Matrix4.look
method is handy when we want to fix the eye at an arbitrary location in the world. However, if we want the viewer to be able to navigate a scene, we need a more sophisticated abstraction that has behaviors for moving and turning. Let's build a FirstPersonCamera
class that manages the viewer's state.
State
The FirstPersonCamera
class must track its position and orientation. Some of the state we have seen as parameters that we'll need to pass to Matrix4.look
. It also holds on to some derived state, like the eyeFromWorld
matrix, which will be frequently accessed by the renderer or other camera methods.
Insert this code in lib/first-person-camera.ts
in your project.
Behaviors
The FirstPersonCamera
class provides several behaviors for initializing the camera, moving and turning it, and building the eyeFromWorld
matrix.
Constructor
The constructor receives the camera's position and focal point and the world's up vector. It directly initializes all but its eyeFromMatrix
and right
variables. Note that we receive the focal point—which is a position. The forward vector is more immediately useful and more flexible, so we store it instead.
The eyeFromWorld
matrix must be rebuilt whenever the camera's position or rotation changes. Instead of duplicating that logic, we factor out a helper reorient
method and call it at the end of the constructor.
Reorient
The reorient
method is called whenever the camera moves or turns. It derives the eyeFromWorld
matrix and right
vector from the camera state using the look
method we defined earlier.
Strafe
We want the camera to move as the viewer interacts with the renderer. One common camera motion in games is the strafe, in which the player sidesteps to the left or right without turning. We strafe the camera by pushing its position some distance along its right vector and then rebuilding the camera's transformation matrix.
If the distance is negative, the camera moves left.
Advance
Another common motion is for the camera to advance forward or backward. We advance the camera by pushing its position along the forward vector.
If the distance is negative, the camera moves backward.
Rotation
Imagine we are piloting an airplane and a flight controller tells us to turn 30 degrees. This isn't a helpful command because we have many possible turns to choose from. We could yaw, which is turning left or right. We could pitch, which is turning the nose up or down. We could roll, which is turning about the nose. Or we could rotate around some non-standard axis.
Cameras are like airplanes in that they may be turned in many directions. Users typically make them yaw and pitch in order to look at nearby objects in the scene. Outside of flight simulators, rolling isn't common.
We yaw the camera by rotating the forward direction around the world's up vector, which is a matrix-vector multiplication. What rotation matrix do we use? Currently our Matrix4
class only has methods for rotating around the x-, y-, and z-axes. The world's up axis could be anything. Let's assume for the moment we have a more general Matrix4.rotateAround(axis, degrees)
method.
We similarly pitch the camera by rotating the forward direction around the right vector.
Since rolling is uncommon, we won't add a method for it.
Put all this code in your FirstPersonCamera
class. We're almost ready to test it out in a renderer. We just need a rotateAround
method.