What is your Alpha/Opacity Mask/Clip/Erode Animation workflow like?


…to recapitulate
from here

Color Burn 1 - (1-dest) / src
Linear Burn dest + src- 1

target = destination (bottom layer)
blend = source (top layer)


What are your thoughts on remapping with a texture atlas instead of using smoothstep? I get more control, but the setup is annoying and the texture read might be slower than smoothstep.


Do you mean a look up texture when you say “texture atlas”? Like a 2D gradient which remaps the values via gradient mapping?


I’m not sure what you mean with remapping with a texture atlas. Do you have a different texture per step of animation? Do you mean lut?

The reasons why I choose to use smoothstep:

  1. Animating from 0 to 1 just makes sense to me, you can go into a curve editor inside of cascade and just animate the value there and it will make sense inside that editor as well.
  2. Smoothstep is an hlsl command.
    (Unreal does this thing where every node is a variable defined as a combination of other variables and values. adding nodes therefore adds a certain cost and complexity to your shader.)
    So it should in theory be more optimised then combining addition and multiplication to achieve the same result. (Not that addition and multiplication are expensive, but still)
  3. Smoothstep is, well, it’s as smooth function, it doesn’t leave ugly lines and artifacts.

Back to your question,

  • Texture atlases are giant and expensive on memory, so I wouldn’t use them in this case.
  • Luts are a great idea however if you want colours, they are so much easier to use then having to manualy manage colours.

Thing is, even when I use luts for something like this, I tend to remap my values first, before I feed them into the lut.


Adds another texture sampler and it’s not very friendly for tweaking because you have to change, save and reload your LUT all the time but here is an example how you can change remapping and timing with just one small 2D LUT:


Oh boi, I should have used the right Unreal terminology “Curve Atlas”, which would save you guys some trouble writing down arguments :slightly_smiling_face:

That’s correct for colors, but I’m talking about only changing grey values, which works like remapping. Basically, you end up remapping 2048 different ranges with 1 single 2048x2048 texture. With that size most artifacts are gone, but sometimes you mix up the range and can get away with less.

I can’t believe you thought I’m talking about actually importing/exporting single grey scale textures when Curve Atlases exist and Jan was praising them like a messiah a while ago :smiley: Anyway, hopefully with the right term you can share another thought :slightly_smiling_face:


This is an excellent description of smoothstep, and is really straightforward and well illustrated!

Wanted to chime in a bit and add a few things that a kind graphics wizard once shared with me.

The output of the hlsl intrinsic smoothstep is actually a curve, which looks sort of like this:
This is excellent if you’re wanting this sort of result but if you’re expecting a linear response between your min and max inputs, be prepared for this!

The under the hood code of the smoothstep looks like this (using float as an example, source)

float smoothstep(float a, float b, float x)
    float t = saturate((x - a)/(b - a));
    return t*t*(3.0 - (2.0*t));

The second line there is the polynomial part that gives you the curve drawn above. If you are arithmetic bound in your shader and want to save instructions, you can cut out that part, and will be left with the linear remapping part of the smoothstep. While it is a hlsl intrinsic, there’s nothing on the gpu hardware that I know of that makes any difference, you’re still paying for all the instructions listed above. Personally, I use this cheaper version 90% of the time, since you can usually not tell the difference and you’re saving instructions on big overdraw heavy particles.

When doing alpha ramps I usually do something like this:

float smoothstep_cheap(float Min, float Max, float A)
	return saturate((A - Min) / (Max - Min));

float RevealRamp(float alphaMask, float revealValue, float revealWidth)
	float widthplusone = revealWidth + 1.0;
	return smoothstep_cheap(1.0f, widthplusone, alphaMask + (revealValue * widthplusone));

This allows me to use a single ‘revealValue’ control, where 0 returns a masked out result, and 1 returns the complete alpha. It also allows you to have a consistent or separately controlled width parameter.

One followup note on the texture/curve lookups - this may be totally the way to go, but it all depends on where your shader bottlenecks are - if you are arithmetic bound, or texture bound. Make friends with your local graphics wizards who, in my experience, can generally help you navigate this stuff!


Hermite interpolation? Wah. :dizzy_face:

If I’m looking at this right, all this is doing is averaging (or perhaps you could say… smoothing) out the values, yes? I mean, wouldn’t that be easier to say for all involved. Man… jargon!

That said… cool. Learned something.


I love this. Using the UV Distort strictly as the shape control, and the LUT strictly as the grade. That is some quality out of the box thinking. Even if this is an old trick, I didn’t know it! It’s new to me!


I learned it from Fallout 4 where they use 2D Lut for Color and also for shaping the alpha: https://simonschreibt.de/gat/fallout-4-the-mushroom-case/


It is very visual though.
If I think about it, I get how this works, but at the same time my head can’t seem to keep track of what is happening, haha.
I’ll have to try it myself at some point :smiley: