Noise-based lightning shader like in Zelda?

In Short, I’m trying to up my nodal skills by recreating a similar effect to how lightning is generally made in Zelda: BotW.

As you can see in the above example, I suspect that it’s made by animating a noise texture, then taking a value of grey on that texture and masking everything above or below. The round, bead-like shapes that fade in into arcs of lightning are what’s tipping me off. The issue with this technique though, is that I don’t see a way to turn this masked information into a nice opacity gradient (short of using HDR and bloom).

So here is the test that I did based on my speculation: I have very little knowledge of the “if” node and reused an example that I found online for what I wanted to achieve.

What are your thoughts on this? Do you think it was made another way? Is there a way to smooth it out? I’m generally new to creating shaders at all, some help would be appreciated :slight_smile:

13 Likes

heads up, I wouldnt use an if node (for pretty much anything that produces a visual end result)
You’ll end up getting some pretty harsh artefacts in this usage case over distances, and adding a smooth falloff isn’t possible.
Not familiar with unity or this node interface, but if there’s a smoothstep node, try using that instead, pretty much in-place of your if nodes.

For extra optimisation i normally ignore the polynomial part of smoothstep and just use the first part of this:

x = clamp((x - edge0)/(edge1 - edge0), 0.0, 1.0);

This normally gets you pretty much a very similar visual without the extra part at the end.

What I’d do, is combine your noise textures together as you’re doing, then a smoothstep where you use a single float to drive the position of the gradient, and another to control the width:
edge0 (min val) = Position - (width)
edge1 (max val) = Position + (width)
x (input) = your noise field

Benefits of doing it like this give you a super easy to adjust falloff on your beams, so if you want a nice soft bloom effect too… it just works. Normally just ramp it how you want and you can get a nice controllable lightning effect. Hardest part then becomes your noise input!

Sorry for unreal nodes, should translate pretty simply though

Small width - (0.001)

Large width - (0.025)

5 Likes

Thanks man, that’s been really helpful indeed.
Still trying to digest the logic of the shader to be honest, but I’m getting there, in the meantime working on the noise texture gives some nice results.

6 Likes

The other modification I might try is applying the vertex alpha to the noise prior to the smoothstep. You could also use fwidth to ensure the blend is always 1 pixel wide. In ShaderForge this is the DXDY node. Not sure what it is in UE4, might have to use DDX and DDY nodes to reproduce fwidth.

The shader code would be something like this:

float width = _WidthParam * fwidth(noise);
float edge0 = noise - width;
float edge1 = noise + width;

The width param would now effectively mean “on screen pixels * 2”.

2 Likes

You beat me to the punch…that was exactly my first thought.

So I am trying to follow this in code and gettiing nowhere…

This is my code

			float4 frag(v2f i) : SV_Target
		{
			float4 col = _Color;
			fixed4 tex = tex2D(_Noise, i.uv);

			float noise = tex.r;

			// how is this mean to animate? The node base images don't show what it lerps between and over what time
			float lerpTime = (sin(_Time * 10) + 1) * .5;
			noise = lerp(noise, noise *2, lerpTime);

			// this returns really harsh, cut out pixelated values
			_WidthParam = .25;
			float width = _WidthParam * fwidth(noise);
			float edge0 = noise - width;
			float edge1 = noise + width;

			float arc= smoothstep(edge0, edge1, noise);

			col *= arc;

			return col;
		}

and this is my results:

any help would be great thanks!

When using fwidth() the _WidthParam is defining the width of the blend in on screen pixels. 0.25 means you’re doing the transition in 0.5 pixels, which is going to be quite aliased. You want 0.5 at the smallest, and more likely between 1 and 0.75 for the smoothest appearance.

fwidth() is also calculating it’s value on blocks of 2x2 pixels, which is what you’re seeing, on top of that 0.25 value.

The biggest problem is I wrote that code as a modification of the shader forge node graph which is doing a bunch of other stuff. To use fwidth with the smoothstep function directly you want to do this:

float width = _WidthParam * fwidth(noise);
float edge0 = _Cutoff - width;
float edge1 = _Cutoff + width;

float arc = smoothstep(edge0, edge1, noise);

Then have _Cutoff as a parameter you control either via the material or by vertex alpha (for particles).

That by itself might still have some odd problems at the min and max _Cutoff values, so I usually scale the _Cutoff with:

float cutoff = _Cutoff * 1.01 - 0.005;
2 Likes

Hi NestyDova how do i make this lightning effect? I really like it. Ive read the thread but its very confusing learning how its made.

What exactly are you having an issue with? Try copying his nodes exactly. If you’re still struggling, try using these as a starting point:

Oh hey @veer, I am actually already using Luos’s Technique like in the video. its just knowing where to add the smoothstep is what I’m struggling with. I want to get that nice smooth looking green lightning effect that @NestyDova has made.

Ah ok. Smoothstep is basically used for erosion lerp between 2 values. So you would basically plug in your alphas and animate it the way you want it. Does that make more sense? I do not have access to Unreal currently I’ll help you with a screenshot once I do.

Alternatively you could also just use a regular alpha erosion similar to this:

That looks neat! but I’m still lost. I made some changes to the texture that Luos was using. I’ll send you my material set up and textures.

The color and intensity pin is in the emissive and the other pin is in opacity:

this is the Texture im using to create variety of lightning: