Visualization of vertex color interpolation using barycentric coordinates

Barycentric coordinates $ (\lambda, \mu, \nu) $ come up in graphics a lot, especially for rasterization since they’re used to interpolate between values at different vertices. So, calculating them quickly is important!

These coordinates have the following properties:

  • $\lambda, \mu, \nu \ge 0 $
  • $\lambda + \mu + \nu = 1$, also sometimes written as $ \lambda = 1 - \mu - \nu $
  • At $p$ (vertex 0), $ \lambda = 1, \mu = 0, \nu = 0 $
  • At $q$ (vertex 1), $ \lambda = 0, \mu = 1, \nu = 0 $
  • At $r$ (vertex 2), $ \lambda = 0, \mu = 0, \nu = 1 $

Before we go over different ways to calculate these coordinates, I want to note that we’re working in device coordinates, i.e. our coordinate vectors are $(\frac{x}{w}, \frac{y}{w}, \frac{z}{w}, 1)$. See the appendix at the bottom for notes on perspective-correct hyperbolic interpolation :)

Planar interpolation

We’ll step through the process, but the general idea is that we set up two planes in a way that lets us plug in an $x$ and $y$ value and get a barycentric coordinate from the $z$ value. It’s pretty convenient for software rasterizers since you can precompute part of this and then quickly calculate $\lambda$, $\mu$, and $\nu$ for each fragment.

First, we start with our 3D device coordinates and project them onto a plane (for a rasterizer, $z=0$ is convenient). We’ll name these projected 2D coordinates $p,q$, and $r$ for the first, second, and third vertex respectively. Also, the math here is assuming that the vertices are counter-clockwise!

Projecting vertices onto $z=0$

Then, we choose the two barycentric coordinates we want to compute. We’ll choose $\lambda$ and $\mu$.

Next, we’ll create two planes (one for $\lambda$ and one for $\mu$). The points we’ll use to compute these planes are our projected 2D coordinates except we’ll use the expected barycentric coordinate for $z$ instead of $0$.

So, for our $\lambda$ plane, we’ll use the points $(p_x, p_y, \lambda=1)$, $(q_x, q_y, \lambda=0)$ and $(r_x, r_y, \lambda=0)$ and our planes should look something like this:

Section of $ \lambda $ plane above projected triangle Section of $ \mu $ plane above projected triangle

Once we’ve constructed our plane, we can just plug in $x$ and $y$ and solve for $z$ to get our barycentric coordinate!

Let’s take a look at the math.

Compute $\lambda$ plane:

Compute points for $\lambda$ plane
$ p_\lambda = (p_x, p_y, 1) $
$ q_\lambda = (q_x, q_y, 0) $
$ r_\lambda = (r_x, r_y, 0) $
Compute $\lambda$ plane equation
$ \vec{n}_ \lambda = (q_ \lambda - p_ \lambda) \times (r_ \lambda - p_ \lambda) $
$ d_ \lambda = \vec{n}_ \lambda \cdot p_\lambda $
$ \vec{n}_ \lambda \cdot \vec{x} - d_ \lambda = 0 $

Also compute the $\mu$ plane the same way as the $\lambda$ plane but with $ p_ \mu, q_ \mu$ and $r_ \mu $ instead.

We’ll want to solve for the $z$ coordinate on these planes:

Re-arrange plane equation to solve for $z$
$ ax + by + cz - d = 0 $
$ cz = d - ax - ay $
$ z = \frac{d - ax - by}{c} $
We can rearrange this into a dot product
$ z = (-\frac{a}{c}, -\frac{b}{c}, \frac{d}{c}) \cdot (x, y, 1) $

Now we can compute $\lambda$ and $\mu$ by plugging in $x$ and $y$. So, calculating all our barycentric coordinates looks like this:

Barycentric coordinate calculation
$ \lambda = ( -\frac{\vec{n}_ {\lambda _ x}}{\vec{n}_ {\lambda _ z}}, -\frac{\vec{n}_ {\lambda _ y}}{\vec{n}_ {\lambda _ z}}, \frac{d_ \lambda}{\vec{n}_ {\lambda _ z}} ) \cdot (x, y, 1) $
$ \mu = ( -\frac{\vec{n}_ {\mu _ x}}{\vec{n}_ {\mu _ z}}, -\frac{\vec{n}_ {\mu _ y}}{\vec{n}_ {\mu _ z}}, \frac{d_ \mu}{\vec{n}_ {\mu _ z}} ) \cdot (x, y, 1) $
$ \nu = 1 - \lambda - \mu $

As mentioned earlier, the nice thing about this method is that we can precompute the first vector for our entire triangle. This means that when we’re deep in our rasterization loop we just have to do two dot products and two subtractions to calculate our barycentric coordinates.


Appendix

Once we have these barycentric coordinates, we can easily turn them into hyperbolic barycentric coordinates with the help of our vertices in clip space.

Calculating hyperbolic barycentric coordinates
$p, q, r$ are in clip space
$ d = \frac{\lambda}{p_w} + \frac{\mu}{q_w} + \frac{\nu}{r_w} $
$ \hat{\lambda} = \frac{\lambda}{dp_w} $
$ \hat{\mu} = \frac{\mu}{dq_w} $
$ \hat{\nu} = \frac{\nu}{dr_w} $