Godot Version
4.4.1 mono
Question
So I’m really new to game dev and shaders. I was trying to make a cool energy shield that kind of has an electric crackle. I followed some shader youtube videos and was playing around trying to trying to mix a basic shield with a electric discharge and i managed to poorly combine the effects(see shader code below.)
In my first attempt I know I messed up the mix and step functions and it just overlaps the effect. Which is not exactly what I want. I’m trying to get the electric discharge to circle the shield outline.
So my second attempt I managed to sort of draw a wavy circle and I can make it black or white by inverting the lighting colour and I can tweek the different raduis to get smaller rings or bigger ones or ones that rotate in opposite directions. But I can’t figure out the math for the correct distance calculation for the lightning colour.
In the original shader the ligntnig can be flipped by changing the dist to use uv X or Y but I don’t understand the math enough figure how to get similar values for dist based on a combination of the x and y to make it diagonal let alone how to make it produce the lighting in a arc/circle
Sheild Code
shader_type canvas_item;
uniform float scale = 25.0;
uniform sampler2D SCREEN_TEXURE : hint_screen_texture, filter_nearest;
void vertex() {
// Called for every vertex the material is visible on.
}
void fragment() {
float time_factor = TIME*1.1;
vec2 modified_UV = UV - .5;
vec2 local_uv = 1.1 * sin(time_factor) * cos(time_factor) * vec2(fract((modified_UV * length(modified_UV)) * scale))-.5;
float large_distance = length(modified_UV);
float grid_distance = length(local_uv);
float grid_alpha = smoothstep(.1, .85, grid_distance) * smoothstep(.5, .44, large_distance)
* smoothstep(.2, .5, large_distance);
float uv_height = SCREEN_PIXEL_SIZE.y / TEXTURE_PIXEL_SIZE.y;
vec2 shield_uv = vec2(SCREEN_UV.x, SCREEN_UV.y + uv_height * UV.y);
vec3 final_colour = mix(vec3(0, 1.0, 1.0), texture(SCREEN_TEXURE, shield_uv).rgb, 0.8);
float final_alpha = grid_alpha * 2.0;
COLOR = vec4(final_colour, final_alpha);
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}
Electric arc
shader_type canvas_item;
uniform vec3 lightning_color: source_color= vec3(.2,.3,.8);
uniform int octave_count: hint_range(0, 20) = 10;
uniform float amp_start=0.5;
uniform float amp_coeff=0.5;
uniform float freq_coeff=2.0;
uniform float speed = .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.5453) - 1.0;
}
float noise(vec2 uv)
{
vec2 luv =floor(uv);
vec2 fuv = fract(uv);
vec2 blur = smoothstep(0., 1., fuv);
return mix( mix(dot(hash22(luv + vec2(0.0, 0.0)), fuv - vec2(0.0, 0.0)),
dot(hash22(luv + vec2(1.0, 0.0)), fuv - vec2(1.0, 0.0)), blur.x),
mix(dot(hash22(luv + vec2(0.0, 1.0)), fuv - vec2(0.0, 1.0)),
dot(hash22(luv + vec2(1.0, 1.0)), fuv - vec2(1.0, 1.0)), blur.x), blur.y) + 0.5;
}
//float noise(vec2 uv)
//{
//return sin(uv.x + uv.y);
//}
float fbm(vec2 uv, int octaves)
{
float value= 0.0;
float amplitude =amp_start;
for(int loop=0; loop<octaves; loop++)
{
value+=amplitude*noise(uv);
uv*= freq_coeff;
amplitude *= amp_coeff;
}
return value;
}
void vertex() {
// Called for every vertex the material is visible on.
}
uniform sampler2D SCREEN_TEXURE : hint_screen_texture, filter_nearest;
void fragment() {
vec2 uv = 2.0 * UV - 1.0;
//uv = vec2(circle(uv, 4));
uv += 2.0 * fbm(uv + TIME * speed, octave_count) - 1.0;
float dist = abs(uv.x);
vec3 colour = lightning_color * mix(0.0, 0.05, hash12(vec2(TIME))) / dist;
//colour = vec3(1.0-colour);
COLOR = vec4(colour, 1.0);
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}
First attempt to combine
``shader_type canvas_item;
uniform float scale = 25.0;
uniform sampler2D SCREEN_TEXURE : hint_screen_texture, filter_nearest;
uniform vec3 lightning_color: source_color= vec3(.2,.3,.8);
uniform int octave_count: hint_range(0, 20) = 10;
uniform float amp_start=0.5;
uniform float amp_coeff=0.5;
uniform float freq_coeff=2.0;
uniform float speed = .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.5453) - 1.0;
}
float noise(vec2 uv)
{
vec2 luv =floor(uv);
vec2 fuv = fract(uv);
vec2 blur = smoothstep(0., 1., fuv);
return mix( mix(dot(hash22(luv + vec2(0.0, 0.0)), fuv - vec2(0.0, 0.0)),
dot(hash22(luv + vec2(1.0, 0.0)), fuv - vec2(1.0, 0.0)), blur.x),
mix(dot(hash22(luv + vec2(0.0, 1.0)), fuv - vec2(0.0, 1.0)),
dot(hash22(luv + vec2(1.0, 1.0)), fuv - vec2(1.0, 1.0)), blur.x), blur.y) + 0.5;
}
//float noise(vec2 uv)
//{
//return sin(uv.x + uv.y);
//}
float fbm(vec2 uv, int octaves)
{
float value= 0.0;
float amplitude =amp_start;
for(int loop=0; loop<octaves; loop++)
{
value+=amplitudenoise(uv);
uv= freq_coeff;
amplitude *= amp_coeff;
}
return value;
}
void vertex() {
// Called for every vertex the material is visible on.
}
vec4 lightningColour(vec2 uv)
{
uv = 2.0 * uv -1.0;
uv += 2.0 * fbm(uv + TIME * speed, octave_count) - 1.0;
float dist = abs(uv.x);
vec3 colour = lightning_color * mix(0.0, 0.05, hash12(vec2(TIME))) / dist;
//colour = vec3(1.0-colour);
return vec4(colour, 1.0);
}
void fragment() {
vec4 lightning_mask = lightningColour(UV);
float time_factor = TIME*1.1;
vec2 modified_UV = UV - .5;
vec2 local_uv = 1.1 * sin(time_factor) * cos(time_factor) * vec2(fract((modified_UV * length(modified_UV)) * scale))-.5;
float large_distance = length(modified_UV);
float grid_distance = length(local_uv);
float grid_alpha = smoothstep(.1, .85, grid_distance) * smoothstep(.5, .44, large_distance)
* smoothstep(.2, .5, large_distance);
grid_alpha = smoothstep(lightning_mask.r, 1.0, grid_alpha);
float uv_height = SCREEN_PIXEL_SIZE.y / TEXTURE_PIXEL_SIZE.y;
vec2 shield_uv = vec2(SCREEN_UV.x, SCREEN_UV.y + uv_height * UV.y);
vec3 final_colour = mix(vec3(0, 1.0, 1.0), texture(SCREEN_TEXURE, shield_uv).rgb, 0.8);
float final_alpha = grid_alpha * 2.0;
final_colour = step( final_colour, lightning_color.rgb);
COLOR = vec4(final_colour, final_alpha);
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}
`
Second attempt to combine
shader_type canvas_item;
uniform vec3 lightning_color: source_color= vec3(.2,.3,.8);
uniform int octave_count: hint_range(0, 20) = 10;
uniform float amp_start=0.5;
uniform float amp_coeff=0.5;
uniform float freq_coeff=2.0;
uniform float speed = .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.5453) - 1.0;
}
float noise(vec2 uv)
{
vec2 luv =floor(uv);
vec2 fuv = fract(uv);
vec2 blur = smoothstep(0., 1., fuv);
return mix( mix(dot(hash22(luv + vec2(0.0, 0.0)), fuv - vec2(0.0, 0.0)),
dot(hash22(luv + vec2(1.0, 0.0)), fuv - vec2(1.0, 0.0)), blur.x),
mix(dot(hash22(luv + vec2(0.0, 1.0)), fuv - vec2(0.0, 1.0)),
dot(hash22(luv + vec2(1.0, 1.0)), fuv - vec2(1.0, 1.0)), blur.x), blur.y) + 0.5;
}
//float noise(vec2 uv)
//{
//return sin(uv.x + uv.y);
//}
float fbm(vec2 uv, int octaves)
{
float value= 0.0;
float amplitude =amp_start;
for(int loop=0; loop<octaves; loop++)
{
value+=amplitude*noise(uv);
uv*= freq_coeff;
amplitude *= amp_coeff;
}
return value;
}
void vertex() {
// Called for every vertex the material is visible on.
}
uniform sampler2D SCREEN_TEXURE : hint_screen_texture, filter_nearest;
void fragment() {
//vec2 uv = 2.0 * UV - 1.0;
////uv = vec2(circle(uv, 4));
//uv += 2.0 * fbm(uv + TIME * speed, octave_count) - 1.0;
//float dist = abs(uv.y);
//vec3 colour = lightning_color * mix(0.0, 0.05, hash12(vec2(TIME))) / dist;
////colour = vec3(1.0-colour);
//
//COLOR = vec4(colour, 1.0);
vec2 uv = 2.0 * UV - 1.0;
//uv = vec2(circle(uv, 4));
uv += 2.0 * fbm(uv + TIME * speed, octave_count) - 1.0;
float dist = abs(uv.x);
//colour = vec3(1.0-colour);
//Calculate distance from center (0.5, 0.5)
float uv_height = SCREEN_PIXEL_SIZE.y / TEXTURE_PIXEL_SIZE.y;
vec2 shield_uv = vec2(SCREEN_UV.x, SCREEN_UV.y + uv_height * UV.y);
vec4 orginalColour = texture(TEXTURE, UV);
dist = length(UV - vec2(0.5, 0.5));
float fbm_val = fbm((UV + TIME) * 20.0 , 10); // Scale UV for more detail
float fbm_val2 = fbm((UV + TIME) * 20.0 , 10);
// Modify radius based on fBM
float dynamic_radius = 0.4 + fbm_val * 0.1; // Adjust range as needed
float dynamic_radius2 = 0.3 + fbm_val2 * 0.1; // Adjust range as needed
float circle_step = step(dynamic_radius, dist);
float circle_step2 = step(dynamic_radius2, dist);
vec3 colour = lightning_color * mix(0.1, 0.05, hash12(vec2(TIME))) / dist;
//colour = vec3(1.0-colour);
COLOR = mix(vec4(colour, 1.0), orginalColour, circle_step);
COLOR = mix(orginalColour, COLOR, circle_step2);
//// Color based on dynamic radius
//if (dist < dynamic_radius && dist > dynamic_radius2) {
//COLOR = vec4(1.0, 0.0, 0.0, 1.0); // Red inside circle
//} else {
//COLOR = vec4(0.0, 0.0, 1.0, 1.0); // Blue outside circle
//}
}
//void light() {
// // Called for every pixel for every light affecting the CanvasItem.
// // Uncomment to replace the default light processing function with this one.
//}