Math 3. Interpolation
3. Interpolation
Lerp
- “Lerp”, or linear interpolation, is a commonly used function in gamedev
- returns the value $x$, which goes from $a$ to $b$, when $t$ goes from $0$ to $1$
- when $t = 0$, $x = a$
- when $t = 0.5$, $x = (b - a) / 2$ (“halfway”)
- when $t = 1$, $x = b$
- In Unity, the most basic lerp function is
Mathf.Lerp(a, b, t)
Lerp example
float x1 = Mathf.Lerp(5.0f, 15.0f, 0f); // returns 5.0f
float x2 = Mathf.Lerp(5.0f, 15.0f, 0.5f); // returns 10.0f
float x3 = Mathf.Lerp(5.0f, 15.0f, 1.0f); // returns 15.0f
Note about Clamping
- what if t is smaller than 0 or larger than 1?
- Unity’s
Mathf.Lerp
is clamps the returned value automatically- $x$ is $a$ at minimum and $b$ at maximum
- with
Mathf.LerpUnclamped
, the value is extrapolated when outside the limits!
Lerping different data types
- Some data types are more complicated than just one float value
- You can of course lerp every number value individually
- Other way around: some types have their own built-in lerps
- Unclamped versions exist, too.
Lerp practical example
- Lerping usually happens in some sort of an update function, where we can use lerp to slowly change a value from
a
tob
, while time moves forward.[SerializeField] Transform endTransform; [SerializeField] float lerpDuration; Vector3 startPosition; float startTime; bool lerping = false; void StartLerp() { startTime = Time.time; lerping = true; } void UpdateLerp() { if (!lerping) return float time = (Time.time - startTime) / lerpDuration; transform.position = Vector3.Lerp(startPosition, endTransform.position, time); if (time > 1) lerping = false; } void Update() { if (Input.GetKey("space")) StartLerp(); UpdateLerp(); }
Exercise 1. Do a lerp!
After pressing a button once, lerp GameObject’s color from red to blue.
Bonus: After pressing twice, lerp the color back to red. Bonus bonus: What if you press the button during lerping?
Other interpolation functions
- Smooth interpolation
- Mathf.SmoothStep: Like Lerp but with smoothing in start and finish
- Mathf.SmoothDamp: Spring-like motion towards destination
- Mathf.MoveTowards: Move linearly towards destination with a max speed limit
- Angle versions (These take into account that the angle loops from 360 back to 0)
Custom interpolation
- Lerping is a linear operation: the rate of change is constant during the process
- Sometimes we want smoothing that is controlled more precisely than with SmoothStep and the like
- Luckily, we can also create custom curves
Custom interpolation with an animation curve
-
for custom interpolation curves, use the
AnimationCurve
variable[SerializeField] AnimationCurve curve;
-
The curve can be manipulated in the inspector:
- Click on the curve images on the bottom to choose and edit them
Controlling values with the curve
- If the curve starts from 0 and ends in 1, you can use it as a replacement for
Mathf.Lerp
- If the curve starts from and ends in 0, you can create an animation that loops back to the initial value!
curve.Evaluate(t)
returns a value from the graph (by default, between 0 and 1)- So we can lerp between a and b if we just supply this to a lerp function!
Mathf.Lerp(a, b, t)
$\Rightarrow$Mathf.Lerp(a, b, curve.Evaluate(t))
Animation curve example
public AnimationCurve bounce;
...
if(startTime > 0) // If we have set a startTime, do the interpolation
{
// Calculate valid time for curve (between 0 and 1)
float time = (Time.time - startTime) / bounceLenght;
// Get the value from curve at the time of the animation
// and multiply it with the desired scaled axis
// then add it to default scale (1, 1, 1)
transform.localScale = Vector2.one + axis * bounce.Evaluate(time);
}
Extra: Deltatime lerping on the fly
- You may have seen lerp performed “on the fly” like this:
transform.position = Vector3.Lerp(transform.position, target.position, Time.deltaTime);
- The start point changes every frame! And instead of a time parameter, there’s
deltaTime
…? What!? - This isn’t what lerp was meant to be used for, but this can be a useful trick
- This creates a deceleration (“braking”) in the end which results in a smoother finish than lerp normally does (and it finishes faster than expected)
- $\Rightarrow$ The interpolation isn’t linear anymore!
- This is often used to make dynamic objects reach an end position gradually
- Example: Camera that follows a bit behind the player character
- This creates a deceleration (“braking”) in the end which results in a smoother finish than lerp normally does (and it finishes faster than expected)
- There’s one important caveat, though…
Deltatime lerping is frame rate dependent!
- This is bad! We get different results with different machines!
- There’s a somewhat-known solution to this
- Instead of
source = Mathf.Lerp(source, target, smoothing * Time.deltaTime);
- Do this:
source = Mathf.Lerp(source, target, 1 - Mathf.Pow(smoothing, Time.deltaTime))
- Instead of
- Read more here: Frame rate independent damping using lerp
- In the link, there’s a techincal explanation why deltatime lerping works how it works, and why the solution above fixes the framerate dependence.
Slerp on-the-fly example
Vector3 relativePos = target.position + new Vector3(0,.5f,0) - transform.position;
transform.localRotation =
Quaternion.Slerp(
transform.localRotation,
Quaternion.Lookrotation(relativePos),
Time.deltaTime
);
transform.Translate(0,0, 3 * Time.deltaTime);
Extra: Inverse lerp
- Script Reference: Inverse lerp
Mathf.InverseLerp(a, b, x)
- Returns the answer to the inverse problem of Lerp:
- “When we know a value $x$ that is between $a$ and $b$, what is $t$?”
- In other words, how far is $x$ between $a$ and $b$, as a fraction
Extra: Remapping with lerp & inverse lerp
- What if we want to map a range $t_0 \dots t_1$ to range $a \dots b$?
- Or in other words, map a variable
input
in rangeinputMin
$\dots$inputMax
- And get a result that is in a range
outputMin
$\dots$outputMax
.
- And get a result that is in a range
- The function that achieves this is a sort of a “generalization” of lerp & inverse lerp:
float Remap (float inputMin, float inputMax, float outputMin, float outputMax, float input) { float t = Mathf.InverseLerp(inputMin, inputMax, input ); return Mathf.Lerp( outputMin, outputMax, t ); }
- If
input
has the value ofinputMin
, the function returns the valueoutputMin
.
- If
Reading
- Learn: Linear interpolation
- gamedevbeginner.com: The right way to Lerp in Unity
- easings.net: a source for different easing functions beyond linear interpolation