Shield with impact waves

Spherical shield with an optional impact generating waves. May be a demanding shader as it does some kind of raymarching for the waves.

Almost everything is a parameter : from the color to the impact location, noise function and wave progression.

Be aware that because of the inexact raymarching function, extreme values can produce artifacts.

Shader code
shader_type canvas_item;

const float MPI = 1.5707966326;

const int STEPS = 20;

const float LOWER_LIMIT = 0.01;

uniform float zoom_out : hint_range(1.0, 10.0) = 1.0;

uniform float border_decay : hint_range(0.0, 0.99) = 0.6;

uniform vec4 shield_tint : hint_color = vec4(0.2, 0.2, 0.5, 0.2);

uniform vec4 shield_saturation : hint_color = vec4(1.);

uniform float attack_angle : hint_range(-6.283185, 6.283185) = 0.;

uniform float attack_penetration : hint_range(0., 0.9) = 0.2;

uniform float attack_radius : hint_range(0., 3.) = 0.2;

uniform float attack_amplitude : hint_range(0., 1.) = 0.1;

uniform float wave_speed : hint_range(2., 40.) = 16.;

uniform float wave_num : hint_range(10., 40.) = 17.;

uniform sampler2D noise_texture;

uniform float noise_speed : hint_range(1.0, 10.0) = 3.;

uniform float noise_amplitude : hint_range(0.01, 1.) = 0.89;

uniform float noise_deformation : hint_range(1., 300.) = 100.;

float compute_z_radius(vec2 pos, float r) {
	vec3 o = vec3(pos, -1.);
	return -sqrt(1. - dot(o, o) + (r * r));
}

float compute_front_z(vec2 pos) {
	vec3 p = vec3(pos, -1.);
	return (-sqrt(2. - dot(p, p)));
}

void fragment() {
	// Sphere computation
	vec2 current_pos = (UV - 0.5) * (2.0 * zoom_out);
	float len = length(current_pos);
	vec2 attack_direction = vec2(cos(attack_angle), sin(attack_angle));
	vec4 noise_texel = texture(noise_texture, current_pos + TIME * attack_direction * noise_speed);
	vec4 noise_amount = (noise_texel * (1. - noise_amplitude)) + noise_amplitude;
	float noise_mask = (noise_amount.r + noise_amount.g + noise_amount.b) / 3.0;
	float amplitude_decay = (1. + attack_amplitude) * border_decay * noise_mask;
	float border_mask = clamp(len - amplitude_decay, 0., 1. - border_decay) / (1. - border_decay);
	float mask = clamp(ceil(noise_mask * (1. + attack_amplitude) - len), 0., 1.);
	vec4 shield_color = mix(shield_saturation, shield_tint, 1. - border_mask) * mask;
	vec2 deformation_mask = (noise_texel.rg - vec2(.5)) * 2. * mask;

	// Waves
	if(len <= 1. + attack_amplitude) {
		vec2 attack_norm = attack_direction * (1. - attack_penetration);
		vec3 attack_position = vec3(attack_norm, compute_front_z(attack_norm));
		float retained_len = 0.;
		float retained_intensity = 0.;
		float z_step = compute_z_radius(current_pos, 1. + attack_amplitude);
		float hdiff = 1. + attack_amplitude;
		float min_diff = hdiff;
		int step_id = STEPS;
		for(int i = 0; i < STEPS; ++i) {
			vec3 current_projection = vec3(current_pos, z_step);
			vec3 pos_on_surface = normalize(current_projection);
			float att_len = length(attack_position - pos_on_surface);
			if(att_len < attack_radius) {
				float intensity = (cos(att_len * wave_num - TIME * wave_speed) + 1.)/2. * cos((att_len / attack_radius) * MPI);
				hdiff = abs(length(current_projection) - 1. - (intensity * attack_amplitude));
				if(hdiff < min_diff) {
					retained_intensity = intensity;
					retained_len = att_len;
					min_diff = hdiff;
					if (hdiff < LOWER_LIMIT) {
						break;
					}
				}

				float extra = pos_on_surface.z * (1. + (intensity * attack_amplitude));
				z_step += (extra - z_step) * (1. - (float(i) / float(STEPS)));
			}
		}
		if ((hdiff < LOWER_LIMIT) || ((step_id == STEPS) && (min_diff < (1.0 + attack_amplitude)))) {
			shield_color = mask*mix(shield_color, shield_saturation, retained_intensity);
			deformation_mask = mask*mix(current_pos * (1. - retained_intensity), deformation_mask, cos(((1. - (len / attack_radius))) * MPI));
		}
	}

	vec4 screen_color = texture(SCREEN_TEXTURE, SCREEN_UV + (noise_deformation * deformation_mask * SCREEN_PIXEL_SIZE));
	COLOR = vec4(mix(screen_color.rgb, shield_color.rgb, shield_color.a), 1.0);
}
Tags
Shield, sphere, water, wave
The shader code and all code snippets in this post are under MIT license and can be used freely. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from CasualGarageCoder

Swirl/Sink

(Almost) Invisible Character

2D Fountain

Related shaders

Shield with impact visualisation

Toon Shader inspired by Genshin Impact

impact shader

guest

0 Comments
Inline Feedbacks
View all comments