Oh that’s from my material. That one is masked and the opacity values are run through a Ceil node before being plugged into Opacity Mask, so with RGB values you see that really dark boarder edge sometimes. This material used the standard Subtract method we all know, but I wanted to adapt this material for use with translucent material instances as well (hence adding in the smooth step)
Super helpful, especially with the visual examples! Thank you for putting all that together
Thank you for this breakdown, I’ve been trying to understand exactly how to apply the smoothstep to my alpha erosion for a while now.
For the Alpha Crawl, there’s Linear Burn and Color Burn. Linear Burn is just a subtract with a 1-x on the bottom input. Color Burn is the same but it divides the result by the inverse of whatever was subtracted. Linear Burn will become dark overall as it fades, while Color Burn will always keep white values white, so you keep your value range, but the length of the gradient will shorten as it fades, resulting in sharper edges. If you want the gradient to be a consistent length, go with Linear Burn.
A lot of the multiplies and other hacks I’m seeing in this thread are just attempts to get a Color Burn blend.
What I like to do is use a Linear Burn, then pass the inverse of that into the emissive, so I get a hot glow on the edge of the crawl that fades as the alpha gets more opaque.
One trick I figured out, in order to fade from opaque to completely gone by lerping 1-0, I multiply the length of my gradient by the lerp alpha and add it to the lerp alpha. This way, a lerp alpha of 1 will always paint the whole mesh white, instead of leaving a little bit of gradient at the end.
Another trick I figured out is to use “equalized” noise textures for my alpha crawls. In Photoshop, go to Image > Adjustments > Equalize. This will make it so an alpha of 0.1 will reveal 10% of the texels, 0.2 will reveal 20%, and 0.9 will reveal 90%, etc. Otherwise, your lerp will have more of an ease-in-ease-out effect, which I’d rather do in the engine if I want it.
…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:
- 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.
- 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) - 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
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 Anyway, hopefully with the right term you can share another thought
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.
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: Fallout 4 – The Mushroom Case | Simon schreibt.
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
Thanks dude, that’s really a new way for me:smiley_cat:
after i saw this viedo https://www.youtube.com/watch?v=yMuDva2Vh9I&t=7s,
i use smooth step too is so good to make dissolve animate
I wanted to extend this topic about a detail. Thanks @Niels for the initial post of the material.
As long as the softness is 0.0 this material works perfect:
But as soon as you use a smoothness you see, that at the beginning of the fading, a small portion is never getting white:
What I like to do: Instead of adding the edgeHardness (which should be called softness) to the Max, I subtract it from Min. Now we fixed the left side but of course the right side doesn’t fade 100% as we want.
To fix that, I add the edgeSoftness to the incoming Alpha Fade values but multiplied by the Alpha Fade value itself:
Now the mask fades in perfectly on both sides and can be as smooth as it wants
Great tweet by Matt Ostertag about erosion:
https://twitter.com/matt_ostertag/status/1488489875766661122?s=20&t=Ikl8gRNtEwOylN4ZCHsIVg
QUESTION
Just a small additional question: I’m getting very different results depending if I saturate/clamp fade+smooth or not. Not sure what’s the better solution.
Without the saturate/clamp the shapes during the fadeout stay smoother:
When adding a saturate the shapes stay harder/brighter during the fade out:
What’s your way?
A new tweet showing another option for doing alpha erosion:
https://twitter.com/Tuatara_Games/status/1620101989983227910?t=QCSJIx5Pet20wcVPU7d2cA&s=09