Lightning
I would once again return to 2D and demonstrate how we can generate an electric discharge effect, which could serve as an obstacle in our space shooter, for example.
The effect features the Fractal Brownian Motion and Perlin Noise.
Find the fully explained tutorial here: https://www.youtube.com/watch?v=WthCMZ1nm2Q
Shader code
shader_type canvas_item;
uniform vec3 effect_color: source_color = vec3(0.2, 0.3, 0.8);
uniform int octave_count: hint_range(1, 20) = 10;
uniform float amp_start = 0.5;
uniform float amp_coeff = 0.5;
uniform float freq_coeff = 2.0;
uniform float speed = 0.5;
float hash12(vec2 x) {
return fract(cos(mod(dot(x, vec2(13.9898, 8.141)), 3.14)) * 43758.5453);
}
vec2 hash22(vec2 uv) {
uv = vec2(dot(uv, vec2(127.1,311.7)),
dot(uv, vec2(269.5,183.3)));
return 2.0 * fract(sin(uv) * 43758.5453123) - 1.0;
}
float noise(vec2 uv) {
vec2 iuv = floor(uv);
vec2 fuv = fract(uv);
vec2 blur = smoothstep(0.0, 1.0, fuv);
return mix(mix(dot(hash22(iuv + vec2(0.0,0.0)), fuv - vec2(0.0,0.0)),
dot(hash22(iuv + vec2(1.0,0.0)), fuv - vec2(1.0,0.0)), blur.x),
mix(dot(hash22(iuv + vec2(0.0,1.0)), fuv - vec2(0.0,1.0)),
dot(hash22(iuv + vec2(1.0,1.0)), fuv - vec2(1.0,1.0)), blur.x), blur.y) + 0.5;
}
float fbm(vec2 uv, int octaves) {
float value = 0.0;
float amplitude = amp_start;
for (int i = 0; i < octaves; i++) {
value += amplitude * noise(uv);
uv *= freq_coeff;
amplitude *= amp_coeff;
}
return value;
}
void fragment() {
vec2 uv = 2.0 * UV - 1.0;
uv += 2.0 * fbm(uv + TIME * speed, octave_count) - 1.0;
float dist = abs(uv.x);
vec3 color = effect_color * mix(0.0, 0.05, hash12(vec2(TIME))) / dist;
COLOR = vec4(color, 1.0);
}
adding this at the end of the fragment function will basically give you a transparent
float alpha = 1.0;
if (color.r <= 0.1){
alpha = 0.0;
}
COLOR = vec4(color, alpha);
Some other ways to get transparency:
COLOR.a = length(COLOR.rgb)/sqrt(3);
works really well, but adding a “2.*” in front (or whatever multiplier to your liking) may be better.
Also:
COLOR.a = 1. – dist;
gives a pretty cool smoky effect that maybe isn’t authentic lightning but looks great. Other cool tweaks:
COLOR.a = dist;
(or better, .5*dist) makes a great “quick & easy” smokiness. Finally, using:
COLOR.a = 1.-length(COLOR.rgb);
inverts it in a pretty neat way. A fun think you can do with this is drop on top of any ColorRect of any color to change the “base color” from white to something else, without otherwise adjusting the shader or setting parameters.
spatial version =)
shader_type spatial;
uniform vec3 effect_color: source_color = vec3(0.2, 0.3, 0.8);
uniform int octave_count: hint_range(1, 20) = 10;
uniform float amp_start = 0.5;
uniform float amp_coeff = 0.5;
uniform float freq_coeff = 2.0;
uniform float speed = 0.5;
float hash12(vec2 x) {
return fract(cos(dot(x, vec2(13.9898, 8.141))) * 43758.5453);
}
vec2 hash22(vec2 uv) {
uv = vec2(dot(uv, vec2(127.1,311.7)),
dot(uv, vec2(269.5,183.3)));
return 2.0 * fract(sin(uv) * 43758.5453123) – 1.0;
}
float noise(vec2 uv) {
vec2 iuv = floor(uv);
vec2 fuv = fract(uv);
vec2 blur = smoothstep(0.0, 1.0, fuv);
return mix(mix(dot(hash22(iuv + vec2(0.0,0.0)), fuv – vec2(0.0,0.0)),
dot(hash22(iuv + vec2(1.0,0.0)), fuv – vec2(1.0,0.0)), blur.x),
mix(dot(hash22(iuv + vec2(0.0,1.0)), fuv – vec2(0.0,1.0)),
dot(hash22(iuv + vec2(1.0,1.0)), fuv – vec2(1.0,1.0)), blur.x), blur.y) + 0.5;
}
float fbm(vec2 uv, int octaves) {
float value = 0.0;
float amplitude = amp_start;
for (int i = 0; i < octaves; i++) {
value += amplitude * noise(uv.xy);
uv *= freq_coeff;
amplitude *= amp_coeff;
}
return value;
}
void fragment() {
vec2 uv = UV; // VERTEX.xy;
uv += 2.0 * fbm(uv + TIME * speed, octave_count) – 1.5;
float dist = abs(uv.x);
vec3 color = effect_color * mix(0.0, 0.05, hash12(vec2(TIME))) / dist;
EMISSION = color;
ALPHA = color.s;
}
[…] based on this shader for 2D lightning. I added some more controls such as thickness and modified it to add alpha […]