Vertex Projection onto a Plane

View all posts

If you want to be notified about future posts, please consider subscribing:


This blog post is a “port” into native website form of a LaTeX/PDF article that I wrote back in 2008, named Vertex Projection onto a Plane. Motivation for that was to play with LaTeX in WordPress. I have not introduced any corrections.


Projection is important graphics operation. We use projection to make our 3-dimensional world appear on a 2-dimensional screen, we use dot product to project one vector onto another. In this article we will derive matrices that project a vertex onto a plane.

If you still do not know what it is for I will give you a brief example. Imagine you want your object to cast a shadow onto a ground plane. You could achieve this by projecting all object’s vertices onto that plane. What you get is your object flattened on the plane just as it would cast a shadow. Such object could be projected from a specific direction (a directional light generating shadows) or from a specific point (a point light generating shadows). We will consider both cases.

1. Directional Projection

Let’s start with some formulas:

x = x_0 + at

y = y_0 + bt

z = z_0 + ct

Ax + By + Cz + D = 0

What we have here is the straight line equation with projection direction [a, b, c], vertex (x_0, y_0, z_0) we are projecting and the plane equation [A, B, C, D] we are projecting onto. Our main task is to find t.

To find t we need to substitute x, y and z equations to the plane equation and solve for t. In other words we want to find such t for which a point on our straight line also lies on the plane. We have:

Ax + By + Cz + D = 0

A(x_0 + at) + B(y_0 + bt) + C(z_0 + ct) + D = 0

Ax_0 + Aat + By_0 + Bbt + Cz_0 + Cct + D = 0

t(Aa + Bb + Cc) = -(Ax_0 + By_0 + Cz_0 + D)

t = - \cfrac{Ax_0 + By_0 + Cz_0 + D}{Aa + Bb + Cc}

At this point we actually have the solution we were looking for. If we substitute this just calculated t value to our initial straight line equation we will have a position (x, y, z) of vertex (x_0, y_0, z_0) projected onto a plane [A, B, C, D] along [a, b, c] direction.

Although we have a proper solution it would be much better to have it in a matrix form. Such a matrix form is convenient, for example, when we use vertex shaders. What we do is just passing the computed matrix to a shader and letting it do the projection in a fast GPU way.

To derive the projection matrix we need to solve x, y and z equations with substituting t and rearrange them a bit. Let’s first solve x for t:

x = x_0 + at

x = x_0 - a\cfrac{Ax_0 + By_0 + Cz_0 + D}{Aa + Bb + Cc}

x = x_0 - \cfrac{aAx_0}{Aa + Bb + Cc} - \cfrac{aBy_0}{Aa + Bb + Cc} - \cfrac{aCz_0}{Aa + Bb + Cc} - \cfrac{aD}{Aa + Bb + Cc}

x = x_0(1 - \cfrac{aA}{Aa + Bb + Cc}) + y_0(- \cfrac{aB}{Aa + Bb + Cc}) + z_0(- \cfrac{aC}{Aa + Bb + Cc}) + (- \cfrac{aD}{Aa + Bb + Cc})

Solving y and z for t in the same way yields:

y = x_0(- \cfrac{bA}{Aa + Bb + Cc}) + y_0(1 - \cfrac{bB}{Aa + Bb + Cc}) + z_0(- \cfrac{bC}{Aa + Bb + Cc}) + (- \cfrac{bD}{Aa + Bb + Cc})

z = x_0(- \cfrac{cA}{Aa + Bb + Cc}) + y_0(- \cfrac{cB}{Aa + Bb + Cc}) + z_0(1 - \cfrac{cC}{Aa + Bb + Cc}) + (- \cfrac{cD}{Aa + Bb + Cc})

Now let’s introduce a helper variable k such that:

k = -\cfrac{1}{Aa + Bb + Cc}

Now:

x = x_0(1 + kaA) + y_0(kaB) + z_0(kaC) + kaD

y = x_0(kbA) + y_0(1 + kbB) + z_0(kbC) + kbD

z = x_0(kcA) + y_0(kcB) + z_0(1 + kcC) + kcD

You may not see it yet but we have just derived our matrix form! Take a look:

\begin{bmatrix} x & y & z & w \end{bmatrix} = \begin{bmatrix} x_0 & y_0 & z_0 & 1 \end{bmatrix} \begin{bmatrix} 1 + kaA & kbA & kcA & 0 \cr kaB & 1 + kbB & kcB & 0 \cr kaC & kbC & 1 + kcC & 0 \cr kaD & kbD & kcD & 1 \end{bmatrix}

And that’s it. Our projection matrix has been derived. Note that the 4th column is made of [0, 0, 0, 1] vector. That means this matrix doesn’t do any perspective transformation so we may be sure that w will always be equal to 1. Dividing x, y and z by w is not necessary.

2. Point Projection

Point projection is very similar to directional. The only thing that is different is that the projection vector is unique for every object’s vertex (x_0, y_0, z_0) and depends on projector’s (point) position (d, e, f). Initial formulas:

x = d + (x_0 - d)t

y = e + (y_0 - e)t

z = f + (z_0 - f)t

Ax + By + Cz + D = 0

So as you can see, the equations are very similar to those used in directional projection. The only change is that we consider projector’s position.

Just as in direction projection we must first find t by substituting x, y and z to the plane equation:

Ax + By + Cz + D = 0

A(d + (x_0 - d)t) + B(e + (y_0 - e)t) + C(f + (z_0 - f)t) + D = 0

(Ad + Ax_0t - Adt) + (Be + By_0t - Bet) + (Cf + Cz_0t - Cft) + D = 0

t(Ax_0 - Ad + By_0 - Be + Cz_0 - Cf) = -(Ad + Be + Cf + D)

t = -\cfrac{Ad + Be + Cf + D}{Ax_0 - Ad + By_0 - Be + Cz_0 - Cf}

Solving x for t yields:

x = d + (x_0 - d)t

x = d - \cfrac{(x_0 - d)(Ad + Be + Cf + D)}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

x = \cfrac{d(Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf))}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)} - \cfrac{(x_0 - d)(Ad + Be + Cf + D)}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

x = \cfrac{d(Ax_0 + By_0 + Cz_0) - d(Ad + Be + Cf) - x_0(Ad + Be + Cf + D) + d(Ad + Be + Cf) + dD}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

x = \cfrac{dAx_0 + dBy_0 + dCz_0 - x_0Ad - x_0Be - x_0Cf - x_0D + dD}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

x = \cfrac{x_0(-Be - Cf - D) + y_0(dB) + z_0(dC) + dD}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

Solving y and z for t in the same way yields:

y = \cfrac{x_0(eA) + y_0(-Ad - Cf - D) + z_0(eC) + eD}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

z = \cfrac{x_0(fA) + y_0(fB) + z_0(-Ad - Be - D) + fD}{Ax_0 + By_0 + Cz_0 - (Ad + Be + Cf)}

And so we have – the matrix form is now easy to be written down. You may be worried about the denominator, as vertex (x_0, y_0, z_0) appears there, so you may think it is not correct. If so you probably forgot about one – perspective division. Dividing by w will be necessary here. The final matrix:

\begin{bmatrix} x & y & z & w \end{bmatrix} = \begin{bmatrix} x_0 & y_0 & z_0 & 1 \end{bmatrix} \begin{bmatrix} -(Be + Cf + D) & eA & fA & A \cr dB & -(Ad + Cf + D) & fB & B \cr dC & eC & -(Ad + Be + D) & C \cr dD & eD & fD & -(Ad + Be + Cf) \end{bmatrix}

After transformation all you have left is to divide x, y and z by w.

3. Acknowledgments

I would like to thank to Krzysztof Kluczek for helping me in developing these formulas (via gamedev.pl forum).

Leave a comment