Ashen Aetna

— Rustily stumbling around on an ash-covered volcano

(A tutorial on/in/about/with 3D graphics, Rust, Vulkan, ash)

Homogeneous coordinates

In the previous chapter we have extended vectors with three components into vectors with four components by adding an additional component of value 1. This was, somehow, helpful for turning translations into linear maps (and I have hinted that it may also be useful in connection with perspective projections) — but we did not look into why this works or what is really going on.

I would like to illustrate the process by some pictures. We went from a three- into a four-dimensional space, and that is hard to draw (or follow, for that matter). Therefore, let’s instead go from one- to two-dimensional spaces. Yep, we’re starting from vectors with one component. That is: from numbers.

Here they are:

These are the “points” . We embed this line into a plane:

The origin of this plane is somewhere outside the line (thick point); I have moved it so that the additional component is just the 1 we had added previously. The meaning of the two basis vectors and in this new plane: describes how to get from one point to the other, so it encodes direction and scale of the line; answers: Where is our line? (Or, more precisely: the 0 on it.)

Now let us try to see how we can describe a translation. We want to shift all numbers (that is: all points on our line) to the right, by .

What happens? Did we change the direction or scaling of our line? No. So, remains .

If we can make this fnuction into a matrix (of size 2x2, because the vectors have two components), that means it should look like this:

What happens with ? Well, we wanted to shift it by . It should, therefore, become . And thus we fill in the missing pieces:

If we try to visualize this transform, we might end up with pictures like the following (for ):


(where for every point in the first picture, a point of the same colour has been drawn at in the second.)

This is not so much a translation, which would rather have resulted in this:

— but rather a shearing.

Restricted to the single line of interest (see the yellow line in the pictures), however, it acts the same. (And it should be noted that every translation on this line can be extended to a shear transform of the plane — and those are linear. This is how we transformed the translations into a linear function.)

Points outside our single lucky line, however, feel the difference between shift and shear. (Pictures: original (gray), sheared (blue), shifted (violet))

Or the points in one system:

While the (blue and violet) circles end up on top of each other, the crosses differ.

Where was moved to , ended up in (not in ). Is there a way to interpret this? Well, let’s have a look (only the results of the shearing):

Indeed, it seems that the two points lay on one line (through the origin) before being transformed (i.e.: the red points), and that also after the transformation both points (i.e.: now the blue points) lie on the same line (through the origin):

(This is not accidental. If , then ; that is: the transform of and of are multiples of each other whenever and are — for every linear map A.)

Now, one can make the case that we should not distinguish between the two red points (or the other points on the red line, for that matter): Was it not a very arbitrary choice that we picked the upper one? We could have taken the lower line (with the lower red and blue point) as our representation of the numbers instead.

That would still go well with our transformation: Whether we claim to map the red point onto the blue point or the red line onto the blue line — that’s not really different.

Let’s make it official: All points lying on the same line through the origin (with the exception of the origin) are the same, from now on. What does that mean in coordinates? That and are “the same” iff they lie on the same line that is iff there is such that

. Or, for : , which works for . Hence: or:

(Do you remember, how in Chapter 13, the two points

	    gl_Position = vec4(0.8,0.4,0.0,2.0);


	    gl_Position = vec4(0.4,0.2,0.0,1.0);

coincided? That is the same principle!)

There is indeed a “real world” setting where identification of all points on a line is natural: Perspective projection. Imagine your eye (or a camera) in the origin. Then all points on the blue line lie in the same direction and cannot be distinguished. (Or rather: you end up seeing only one of those points: the closest one where there is something to see and not just (transparent) air.)

That’s why these coordinates also fit so well with the perspective projection mentioned in the previous chapter. We will, at some later time, probably return to projections, so let’s not go into more detail here.

But I think, by now we have seen where this fourth component in coordinates in our shader comes from and, roughly, what it means.

Let us return to our shader.