in Maths, Tutorial

Linear Interpolation

This tutorial will explore one of the most used Mathematical tools in Game Development: linear interpolation! This first post will focus on its Mathematical definition and implementation. The following parts of this series will explore how to extend linear interpolation to non-linear mappings, and how to use them to correct colour curves.

You can find a link to download the C# scripts and the Unity package used at the end of this post.

Linear Interpolation

One of the most useful—and somewhat underrated—functions in Game Development is lerp. Shorthand for linear interpolation, you can imagine lerp as a way to “blend” or “move” between two objects, such as points, colours and even angles.

Virtually every software comes with a function to perform linear interpolation. Unity, for instance, has several; the most well-known being being Mathf.Lerp. It takes two numbers, namely a and b, and lerp them using another numbers t which value must be between 0 and 1.

The result is simple: when t=0, the result is a; when t=1, the result is b; when t=0.5, the result is the average between the two, \frac{a+b}{2}; and so on.

In a nutshell, the parameter t allows to move in the range between a and b. Mathematically, we can say that lerp is a function that maps the interval \left[0, 1\right] to the interval \left[a, b\right], and it can be expressed using the following equation:

(1)   \begin{equation*} c = a + \left(b-a\right)*t\end{equation*}

This equation might seem confusing at first, but it has a very simple geometrical interpretation, as seen in the diagram below. The value of t can range anywhere from 0 to 1. Once multiplied by b-a, it will range between 0 and b-a: exactly the size of the desired interval. Then, all that is needed is to shift it accordingly by a.

Inverse Lerp

Some libraries also feature a complementary function; Mathf.InverseLerp, in case of Unity. As the name suggests, inverse lerp does exactly the opposite of what lerp does: it remaps a number in the interval \left[a, b\right] to the interval \left[0, 1\right].

(2)   \begin{equation*} t = \frac{c - a}{b-a}\end{equation*}

A degenerate case can occur when a=b, as that would lead to a division by zero. In that case, it is not uncommon to assume t=0.5.

Even (2) can be interpreted geometrically. In this case, the exact same operations of lerp are preformed, but in reverse:

Linear Mapping

With these two functions, it is possible to remap any arbitrary value x \in \left[a, b\right] to any other integer interval \left[c, d\right].

(3)   \begin{equation*} y = c + \frac{d-c}{b-a} \left(x - a\right)\end{equation*}

which, once again, has a very immediate geometrical interpretation:

📚 An alternative derivation
The equation for the complete linear interpolation, (3), can also be derived in a more algebraic way.

What linear interpolation does, is to keep the proportion between x with respect to \left[a, b\right] equal to the proportion between y with respect to \left[c, d\right]. We can imagine the two intervals as “loading bars”, and x and y the two cursors that indicate which percentage of the loading is done. Given x, linear interpolation asks to find the value of y so that both “loading bars” are showing the same percentage.

This “percentage” is calculating dividing the “current loading” by the “total loading”. And ensuring that both quantities are set to be equal for both intervals:

(4)   \begin{equation*}\frac{x-a}{b-a}=\frac{y-c}{d-c}\end{equation*}

Solving for y, yields the expression previously described in (3).

Some libraries wrap those two functions into one; Arduino, for instance, calls this map. No equivalent function is present in Unity, but it can be easily implemented like this:

float y = Mathf.Lerp(c, d, Mathf.InverseLerp(x, a, b));

Or, slightly more efficiently, like this:

public static float Lerp (float x0, float x1, float y0, float y1, float x)
{
    float d = x1 - x0;
    if (d == 0)
        return (y0 + y1) / 2;

    return y0 + (x - x0) * (y1 - y0) / d;
}

Linear interpolation is indeed linear because it creates a linear relationships between between the input and output intervals. If we look at lerp as a mathematical function plotted in the cartesian plane (below), it is easy to see that it represents the equation of a line that passes between \left(a, c\right) and \left(b, d\right):

⭐ Suggested Unity Assets ⭐
Unity is free, but you can upgrade to Unity Pro or Unity Plus subscriptions plans to get more functionality and training resources to power up your projects.

Lerping Everything…

For instance, lerping between two points means moving along the shortest path that connected them. On the other hand, lerping between two colours means blending

Points

So far, we only interpolated two numbers. But nothing stops us from interpolating two points. In this case, the easiest way to do so is to interpolate the individual X and Y coordinate:

Vector2 Lerp (Vector2 a, Vector2 b, float t)
{
    return new Vector2
    (
        Mathf.Lerp(a.x, b.x, t),
        Mathf.Lerp(a.y, b.y, t)
    );
}

Unity comes with both Vector2.Lerp and Vector3.Lerp which, as expected, allow to interpolate between points in 2D and 3D. Lerping between points, regardless of their dimension, is equivalent to moving along the line that connected them.

While lerp is inherently linear, it can actually be used to create curves. Successive interpolations on a series of points results in a family of shapes known as Bézier curves (pronounced: /ˈbɛz.i.eɪ/, BEH-zee-ay).

Vector2 Bezier (Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t)
{
    // Lerp between the control points
    Vector2 a = Vector2.Lerp(p0, p1, t);
    Vector2 b = Vector2.Lerp(p1, p2, t);
    Vector2 c = Vector2.Lerp(p2, p3, t);
    
    // Lerp between the lerped points
    Vector2 d = Vector2.Lerp(a, b, t);
    Vector2 e = Vector2.Lerp(b, c, t);

    // Lerped between the lerped points (again!)
    return Vector2.Lerp(d, e, t);
}

The Ever so Lovely Bézier Curve by Shader Witch Freya Holmér is possibly the most accessible article on the topic (and the source of the animation above).

Colours

Another very common way in which lerp is used is to blend colours. Conceptually, you can imagine lerping between colours as mixing different amounts of paint. Lerping between red and blue with t=0.8, for instance, is a bit like filling a can with 20% of red paint and 80% of blue paint.

The easiest way to interpolate between colours is to interpolate the single red, green and blue components independently:

Color Lerp (Color a, Color b, float t)
{
    return new Color
    (
        Mathf.Lerp(a.r, b.r, t),
        Mathf.Lerp(a.g, b.g, t),
        Mathf.Lerp(a.b, b.b, t),
        Mathf.Lerp(a.a, b.a, t)
    );
}

That is exactly how Color.Lerp works in Unity. While this technically works, it often yields rather poor results. This is because the RGB colour space is good to store colours, but not so much to manipulate them in a way that makes sense, perceptually.

Learning how to properly interpolate between colours is a dark art, which is heavily discussed in The Secrets of Colour Interpolation. You can play with the swatches below to see for yourself what difference it makes to lerp between different colour spaces.

Rotations

The last aspect that this first post is going to discuss, is about angles and rotations. From what we covered so far, one might be tempted to lerp between angles in order to perform rotations. That is actually not going to work; or at least, not always, and not as you expect.

The reason is that angles are “looping”, and being able to take that into account requires a non-linear operator. Let’s make a simple example: lerping between 90° and 180° should work as intended, giving you a nice angle that goes from 90 to 180. However, lerping from 350° to 10° will not! There are two ways to reach 10 from 350: going up, or going down. The linear interpolation does not know that it is working with angles, and it will take the longer path, decreasing the angle from 350 to 10.

One way to fix this is to use quaternions instead. Quaternions are an alternative—and somewhat safer—way or representing and working with angles in 3D. But they are also incredibly counterintuitive and difficult to grasp.

Unity comes with its own way to lerp between quaternions, called Quaternion.Slerp. The term slerp stands for spherical linear interpolation. In a nutshell, slerp allows to lerp between any two points on a sphere, rather than a flat plane. A future series will delve into the absolute madness that are quaternions; luckily for you, this is not that series.

What’s Next…

The second part of this series will see how the linear interpolation can be extended to non-linear functions.

Download Unity Package

Become a Patron!

The Standard package contains the script to perform piecewise linear interpolation. It uses extension methods which allows to easily interpolated numbers, vectors, colours and even quaternions! The Advanced package, instead, contains a test scene which also shows how to correct colour curves.

FeatureStandardAdvanced
Linear Interpolation
Piecewise Interpolation
Test scene
Colour Curve Correction

💖 Support this blog

This websites exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Twitter_logo

YouTube_logo
📧 Stay updated

You will be notified when a new tutorial is relesed!

📝 Licensing

You are free to use, adapt and build upon this tutorial for your own projects (even commercially) as long as you credit me.

You are not allowed to redistribute the content of this tutorial on other platforms. Especially the parts that are only available on Patreon.

If the knowledge you have gained had a significant impact on your project, a mention in the credit would be very appreciated. ❤️🧔🏻

Write a Comment

Comment

  1. You don’t need to use an exotic colour space like LCH to get quality lerping between colours, you just need to use a linear colour space. RGB is fine as long as it’s Linear RGB. If you’re not sure if your RGB colour space is Linear RGB, it almost certainly isn’t.

    If you’re using non-linear (“Gamma”) RGB and you want to do good quality linear interpolation, the easiest solution is to convert to Linear, do the interpolation, and then convert back to Gamma. A quick and dirty approximation to convert from Gamma to Linear is to normalize your RGB values to to range 0.0 – 1.0, and then take the square root. To convert back to Gamma you square each value (and multiply by 255 if needed). Depending on the actual colour space of your target device this conversion probably isn’t exactly right, but it’s still a lot better than interpolating in Gamma space.

    Unity has an option to use a Linear RGB colourspace, which you should turn on if you can to get much better quality lighting (but it isn’t supported on some target platforms). I don’t know if this affects the behaviour of Color.Lerp but that should be easy to check.

Webmentions

  • Colour Curve Correction - Alan Zucconi May 2, 2021

    […] Part 1: Linear Interpolation […]

  • Piecewise Interpolation - Alan Zucconi May 2, 2021

    […] Part 1: Linear Interpolation […]