The Myth of Variable Smoothstep

Recently I’ve seen and taken part in a few searches for an interpolation function called Variable Smoothstep. I think I’ve found an answer, but let’s start from the beginning:

Interpolation crash course

Interpolation is the process of getting from A to B without jumping. Let’s take a look at some simple ways of doing this. The most commonly used interpolation function in video games is linear interpolation, or lerp(). In normalized form, it is y=x and looks like this:
Lerp
Note: normalized forms simply interpolate between 0 and 1, and are useful for the type of comparison we’ll be doing.

Lerp is great because it’s simple, but for some situations it is too crude. It starts and stops abruptly, giving the transition a robotic feeling. To provide a smooth start and stop (also known as easing in and out), we can use a cubic hermite function known as smoothstep(). Smoothstep is more complex than lerp, but still a relatively simple function. Normalized, it can be written y=-2x3+3x2 and looks like this:
Smoothstep
Smoothstep is simple and useful. It has a slope of zero at its endpoints, so it gets you from A to B without giving you whiplash. Because it doesn’t move as fast as lerp near the endpoints, smoothstep has to move faster at its midpoint, where it has a slope of 1.5.

The next function we’ll look at is a degenerate case of interpolation called step(). Step doesn’t get used very much in video games because it doesn’t provide continuous motion. Instead, it snaps from the origin to its destination at the half-way point:
Step
Although rarely used in practice, step is important to understanding the next goal.

Interpolating between interpolations

Smoothstep is great, but it has a fixed shape. Maybe you want an even easier start and finish with a faster transition in between. Perhaps you’d like to slow down in the middle in exchange for starting and stopping more abruptly. Looking at the smoothstep curve, it seems like a slightly curved version of lerp. Maybe the slope at the halfway point could be varied arbitrarily, letting you go all the way from lerp (which gets to full speed immediately) through smoothstep (with its gentle acceleration) to step (with infinite, instantaneous speed).

The desire for this kind of flexibility is the genesis of Variable Smoothstep. In addition to the three arguments that all interpolation functions have (start and end points, and x, the interpolation parameter), variable smoothstep would introduce a fourth parameter which determined the slope at the midpoint. Let’s call this fourth parameter s. By providing different value for s, we would like to get different slopes at the midpoint, and have the rest of the curve twist to accommodate:
Variable midpoint slope
This is great, because it lets you interpolate as smoothly or abruptly as you want rather than having to pick a single function. Ideally, though, we wouldn’t be providing the actual midpoint slope because it has the awkward domain of [1,∞]. Instead, we would like it to range from zero to one with the following results (we’ll call it v now because it is no longer slope):
Variable Smoothstep
We’ve got the specifications for our desired function. Now all we need is the math. A bit of Googling shows some unsuccessful attempts, and nothing really promising. I have a feeling that while a fair number of animators, game programmers and possibly audio engineers have come across the need for variable smoothstep, the problem hasn’t received the attention of anyone with a solid background in mathematics. I’ve asked a few people who are better at math than I am, but we’ve never been able to arrive at an elegant (or even convincing) answer.

Unfortunately, everyone who wants variable smoothstep, as defined above, has accidentally created a trick question. This meta-interpolation function looks like it should be a natural progression, with the midpoint slope moving gently from 1 to infinity. The problem is that the definition glosses over what happens at the endpoints. Step and smoothstep both have endpoints with slopes of zero. Lerp, however, has the same slope at its endpoints as it does in the middle, which is 1. This means that there is no continuous way to get from lerp to smoothstep that will also take you from smoothstep to step.

Something’s got to give

Simply put, there is no sensible way to define a variable smoothstep that fulfills the above requirements. Instead of trying to get all three behaviours out of one function, it might be better to interpolate between two of them. Which functions you want to start with depends on the behaviour you want, but it will be easier to do without accidentally creating impossible requirements.
Comments