A Virtual Camera

Viewing the raytracer's output of whatever scene is being processed is a critical aspect of the software. An easy and oft-used first check of new additions to a raytracer is the simple "does it look right?" test, for which some method of displaying the output is required.

A common way to provide such output is by constructing a virtual camera. We define this camera as follows:

  • Vector origin, representing the position in world space of the camera
  • Vector direction, representing the direction the camera points
  • Vector up, representing the orientation of the camera
  • Real fov, the field of view of the camera
  • Real aspect ratio, a ratio of the width of the image to its height

Incorrect Camera CalculationsAfter constructing this camera, the raytracer itself uses the camera's origin as the origin for the rays it shoots, pointed in a direction based off of the other camera variables. In every raytracer I have ever built (and I have a hunch this holds true for most people), I've flipped some negative or plus sign and ended up debugging for thirty or forty minutes. This time around, I flipped the order of a cross product and ended up with the strange results in the accompanying image. Come to think of it, this "flipping the order of a cross product" is a theme in raytracing, and will pop back up when we discuss normals in a later section.

Corrected Camera RotatingAfter fixing this, the camera worked well! The image below is a sequence built by rotating the camera in a circle about the (world-space) origin. Below is the math used to determine the basis vectors for the camera, as well as how to shoot a ray from the camera origin out through a pixel in the final image.

// Compute the basis vectors for our camera space
this->w = look;
this->u = up.cross(w);
this->v = w.cross(u);
this->s = aspect_ratio / (2 * tan(fov));

// Create a ray shot from the camera through a point <x,y>
Vector direction;
direction.x = u.x*(x*0.5*aspect_ratio) + v.x*(y*0.5) + w.x*-s;
direction.y = u.y*(x*0.5*aspect_ratio) + v.y*(y*0.5) + w.y*-s;
direction.z = u.z*(x*0.5*aspect_ratio) + v.z*(y*0.5) + w.z*-s;
Ray* r = new Ray(origin, direction, Color());