1
$\begingroup$

This question perhaps has been asked and answered a thousand times, and yet I haven't found any that satisfy me. The reasons are often these:

1/ You need a 4 dimensional vector to work with 4x4 matrices.

2/ You need to have z stored in some way so that you could later use it for z-buffering, etc.

3/ You need a matrix that is independent of z so that you could multiply matrices together and save computations. Instead of having to multiply each vector with each of the matrix, you could multiply 1 result matrix with each of the vector.

Here's the problems I have with each of them.

  1. We can pretend that the 3 dimensional vector is 4 dimensional (imaginary w = 1). This is after all Computer Science, not math.

  2. When multiply matrices with vector, we don't need to crush z. Keep it and use it. When drawing, we can pretend that z is already crushed.

  3. You will have to do division by w after projection matrix anyway. Why not divide by z then and get something like [x/z, y/z, z] (I'm not dividing by z because it will be used for z-buffering). Both of them cost 1 computation.

So why do we need homogenous divide?

$\endgroup$
1
  • $\begingroup$ "So why do we need homogenous divide?" Perhaps the questions you need to ask yourself are 1) what are the alternatives? 2) are they more efficient or more amenable to hardware implementation? $\endgroup$ Commented Feb 15, 2018 at 1:54

3 Answers 3

7
$\begingroup$

First of all we need to understand why do we need 4x4 matrices in the first place. With 3x3, we couldn't represent translation as it wasn't a linear transformation (it displaces the origin). So in order to avoid extra work, homogeneous coordinates and affine transformation was introduced.

Now instead of doing

$v' = Lv + t$

where L is a linear transform and t is the translation, we can do

$v' = Av$

Where A is the affine matrix. This makes it cleaner. So 4x4 matrices are a real necessity, we just can't work without them. In order to distinguish between vectors and points we use w = 1 for points and w = 0 for vectors. So you are suggesting to make this 4th dimension implicit and don't store it as it'll actually use space/memory.

We can pretend that the 3 dimensional vector is 4 dimensional (imaginary w = 1). This is after all Computer Science, not math.

This works but up-to a certain extent. Using this approach we would have 4x4 matrices and 3 dimensional row/column vectors. But now when doing multiplication we will have to make a check whether it's point or vector and based on that we multiply the elements of the 4th column of the transformation matrix by 0 or 1.

This alone starts creating mess, check every time whether it's a point or a vector so why not just use the space for a single float more and get rid of this stupid check.

That aside most modern CPUs have SIMD registers 128 bits wide perfect to fit 4D vectors. If you are doing your calculations on the GPU, all the more reason to store 4D vectors as branching instructions are much more costly on the GPU.

If you start feeling the need to use 4D, great. If not then we still have the problem of composing matrix transformations. Instead of multiplying individual transformations with our vector we would like to pre-multiply all of the transformations then do a single vector-matrix multiplication. This can't work with projective transformations where we need to divide by "something"

Let's come to reason number 3.

You will have to do division by w after projection matrix anyway. Why not divide by z then and get something like [x/z, y/z, z]. Both of them cost 1 computation.

This is the simple case for just a projective transformation. Consider the vector going through 5 or 6 transformations and in the last comes the projective transformation. If we pre-multiply all these transformation to create a single matrix, you will notice that now when we multiply the vector with this combined transformation matrix the division factor isn't just a simple -z value. The 4th row of the matrix won't be 0 0 -1 0 as in the standard projection matrix. It might have changed due to multiplying all the transformations together. Now when you multiply that 4th row with the 4D vector, you will get your w value by which you need to divide now.

$\endgroup$
5
  • 1
    $\begingroup$ Good answer. I'd add to the last part, even in the case of a simple projection transform with no other matrix multiplies the Z component doesn't carry over untouched - it ends up in the form A*Z + B, used for near and far clipping and perspective-correct interpolation. So even in the simplest case you still need to store the original Z value somewhere. $\endgroup$
    – russ
    Commented Feb 22, 2018 at 9:43
  • $\begingroup$ @russ - true that, but thinking from his perspective, even tho the final Z value is A*Z + B, we still have the original vector IF we aren't overwriting/ storing the modified vector in the original variable. So he could argue we still have the original Z value stored. That's why tried to give an explanation where the division factor isn't just "-z" it's something entirely else. $\endgroup$ Commented Feb 22, 2018 at 19:32
  • $\begingroup$ "why not just use the space for a single float more and get rid of this stupid check" Why not let the type system keep track of which things are points and which vectors at compile time, and get rid of this stupid extra float that needs to be multiplied and added at runtime 😉 $\endgroup$ Commented Feb 22, 2018 at 21:30
  • $\begingroup$ Also FYI, to the point about SIMD, storing an xyzw vector as a SIMD register actually isn't best practice. Better is to go SOA and have one register of 4 vectors' X values, another of the 4 Y values, etc. This generalizes a lot better to larger SIMD widths. Note that a lot of newer CPUs have 8-wide SIMD, and GPUs have 32- or 64-wide SIMD. $\endgroup$ Commented Feb 22, 2018 at 21:37
  • $\begingroup$ @Nathan Reed - don't understand what you meant by "type system" in your first comment? If you meant something like separate classes for points and vector each having a * operator defined for itself and a matrix and the actual 1 or 0 hardcoded inside that function, then it isn't applicable in for example shaders. $\endgroup$ Commented Feb 23, 2018 at 9:10
1
$\begingroup$

Some very quick answers:

We can pretend that the 3 dimensional vector is 4 dimensional (imaginary w =1). This is after all Computer Science, not math.

Ahh.. but sometimes in graphics we don't have w=1. Some examples that come to mind are

  1. Placing a vertex at "infinity", e.g. for shadow volumes
  2. Representing directions rather than absolute positions
  3. Doing rational curves. e.g. $\frac{Quadratic Bezier()}{Quadratic Bezier()}$ to represent, say, conic sections.

I'm sorry, I've no idea what you mean by "to crush z", but sometimes, if chosen carefully, after the projection transform you may be able to get by with just 3 terms, e.g. (X/W, Y/W, 1/W) assuming you also have the post-projection texture coordinates, (U/W, V/W).

I'm not dividing by z because it will be used for z-buffering

Typically, despite the name, the Z-buffer contains an interpolated 1/W, 1/Z, or some linear function of one of these.

-

$\endgroup$
2
  • $\begingroup$ By crush z, I mean making them 0 to make points become 2d $\endgroup$ Commented Feb 14, 2018 at 13:45
  • $\begingroup$ @ManhNguyenHuu: But the points never "become 2d". That's the point. They're always three-dimensional positions, even in window-space. $\endgroup$ Commented Feb 15, 2018 at 1:49
0
$\begingroup$

We use projective geometry and homogeneous coordinates so that we can make use of linear algebra to do our transformations.

And by the way, this is math. I think you should read wikipedia's pages about Projective Geometry, and the clever idea to introduce a Point at Infinity, and use Homogeneous Coordiantes to allow you to deal with projective geometry transformations using linear algebra as opposed to dealing with the ugly math required to do transformations after the divide (because projections are non-linear).

At some point, you must divide though, so this technique is a way of deferring that divide to the very end. All the other transformations can be dealt with in a linear fashion with nice simple matrices and such - even though we are dealing with projection. In terms of OpenGL (and others) we only divide when we go from Clip Space to Normalized Device Space - long after all the 3D transformations have been applied.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.