Homogeneous Coordinates
Points and vectors represent two distinct ideas. A point is a static position, while a vector is a direction not tied to any particular position. In a 2D game, we might place the player at point \((5, 2)\) looking up and right along vector \((0.8, 0.6)\). If the player takes a few steps to the right but doesn't turn their head, they are now at point \((7, 2)\), still looking along vector \((0.8, 0.6)\).
Vectors also include a scalar quantity measuring some phenomenon like force or distance along the vector's direction. Sometimes we do not care about this magnitude and set it to 1 to simplify calculations.
Given these differences, we should create a Point3
class to model points and a Vector3
class to model vectors. However, points and vectors have the same shape: each is a tuple of \(n\) numbers. Graphics developers tend to use a single vector class for both. We will do the same, but we must be careful. Not every method in Vector3
is mathematically valid for both points and vectors. For example, points don't have a magnitude, but vectors do. Points can't be added, but vectors can. The type system won't catch our illegal expressions since it will see everything as a vector.
As we learn more graphics algorithms, we will sometimes find that we do need to distinguish between points and vectors. Instead of creating separate classes, we employ a different a trick: we add an extra dimension to the tuple. The extra number is called \(w\) or the homogeneous coordinate. Points have 0 for their w-coordinate. Vectors have 1. The 2D point \((-4, 2)\) is \((-4, 2, 0)\) in this higher-dimensional space, and the 3D vector \((9, 4, 2)\) is \((9, 4, 2, 1)\).
In two dimensions, points moved into homogeneous space fall on the plane \(w = 0\), and vectors fall on the plane \(w = 1\). What if \(w\) is something besides 0 or 1? We say these are also vectors, but they are not in standard form. To standardize a homogeneous vector—to make \(w\) become 1—we scale all the coordinates uniformly by dividing them by w. The vector \((10, 6, 2)\) becomes \((5, 3, 1)\). The vector \((3, 8, 0.5)\) becomes \((6, 16, 1)\). The vector \((x, y, w)\) becomes \((\frac{x}{w}, \frac{y}{w}, \frac{w}{w}) = (\frac{x}{w}, \frac{y}{w}, 1)\).
Experiment with this homogeneous coordinates renderer. Enter various values of x, y, and w to see how the coordinates are interpreted as points, standard vectors, and non-standard vectors.
Non-standard vectors are displayed in both their unscaled and scaled forms.
The Vector3
class is still important to have in our graphics library, but to add the w-coordinate, we'll eventually need a Vector4
class. We'll write it in lab.
Summary
We share virtual 3D worlds with each other by reducing them to simpler models; assembling the models into points, lines, and triangles; rasterizing their geometry into a framebuffer; and displaying the pixels on screen. Graphics platforms like WebGL handle many of the low-level rendering tasks, and a large part of being a graphics developer is learning these platforms and building abstractions on top of them. Already we have several such abstractions: Vector3
, VertexAttributes
, ShaderProgram
, and VertexArray
. We put the vertices of our models into vertex buffers. On a draw call, the GPU pulls vertices out of the vertex buffers and sends them through the vertex shader. During rasterization, the pixels between between the vertices are colored by the fragment shader. We represent points and vectors with the single Vector3
class. To distinguish between them, we add a homogeneous coordinate.