Step and pulse functions (for alpha channels)

Artists get a lot of mileage out of animating the alpha reference value for the alpha test. Lets call the alpha reference value t. Then, for example, by having alpha come from a 0..1 alpha “contour” or “depth” map, artists can make things gradually appear or disappear by animating t.

What the alpha tests implements is effectively the Heaviside step function, except centered at t instead of at 0. You also find this implemented e.g. in the step() function of the Renderman shading language:

float step(float t, float x) {
    return (float)(x >= t);
}

The alpha test is great for cutout alpha, but if you have a shader system that does layering within a single shader, having a “reference alpha” feature for the blending of each layer in the shader also makes a lot of sense. We can easily implement the step function in a shader as per above, but it is easy enough to support a small palette of alpha filtering functions that an artist can apply to the alpha values read from an alpha channel (or similar).

The picture below shows a few interesting functions to map an input alpha, x, into an output alpha, y, with control parameters t and w:

Alpha filter functions

The last function above is the traditional step function. (The functions in blue can be ignored; they are only there to help show the function derivations.)

It is worth noting that the square pulse() function can also be implemented in terms of subtracting two step() functions:

Square pulse function

We can use the same technique to come up with expressions for most of these functions. For example, the triangle pulse can be expressed as:

Triangle pulse function

In addition to these simple functions that can be implemented in around 1-3 shader “instructions”, we can also provide smooth filtering functions. I’m not covering the smooth functions here, because, well, I don’t have a nice way of plotting them accurately!

That said, at least be familiar with the smoothstep() function:

float smoothstep (float min, float max, float x) {
    x = saturate((x - min) / (max – min)); 
    return x * x * (3 - 2 * x);
}

(Follow the link to see what it looks like.)

Which functions does your engine support?

Leave a Reply