Glowy outline with screen space Chromatic Aberration

Made with Godot 4.5

Bloody hell… I shoved everything into one 😭

Btw, this shader can give a TextureRect outline, glow and even turn them into transparent with screen space chromatic effect (OPTIONAL)

It also simulates the frosted glass surface so the transparent to make the node more visible

For easy control over the effect, I have “intensity” uniform so you don’t have to deal with so many lines of code just to adjust this shader 😀

 

Shader code
shader_type canvas_item;

// ============================================================================
// MASTER CONTROL
// ============================================================================
uniform float intensity : hint_range(0.0, 1.0) = 1.0;

// ============================================================================
// OUTLINE
// ============================================================================
uniform float outline_width : hint_range(0.0, 50.0) = 2.0;
uniform vec4 outline_color : source_color = vec4(1.0);

// ============================================================================
// GLOW
// ============================================================================
uniform float glow_size : hint_range(0.0, 100.0) = 10.0;
uniform float glow_intensity : hint_range(0.0, 5.0) = 1.0;
uniform vec4 glow_color : source_color = vec4(0.5, 0.8, 1.0, 1.0);

// ============================================================================
// SCREEN-SPACE CHROMATIC DISTORTION (OPTIONAL)
// ============================================================================
uniform bool enable_screen_space = false;
uniform float distortion_strength : hint_range(0.0, 0.1) = 0.0;
uniform float chromatic_strength : hint_range(0.0, 0.05) = 0.0;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_linear;

// ============================================================================
// FRAGMENT
// ============================================================================
void fragment() {

	vec2 pixel = TEXTURE_PIXEL_SIZE;
	vec4 base = texture(TEXTURE, UV);
	float alpha = base.a;

	vec4 color = base;

	// Skip heavy work when intensity is zero
	bool active = intensity > 0.0;

	// ------------------------------------------------------------------------
	// OUTLINE
	// ------------------------------------------------------------------------
	float outline = 0.0;

	if (active && outline_width > 0.0 && alpha < 1.0) {
		int radius = int(ceil(outline_width));
		float max_alpha = 0.0;

		for (int x = -radius; x <= radius; x++) {
			for (int y = -radius; y <= radius; y++) {
				if (x == 0 && y == 0) continue;

				float dist = length(vec2(float(x), float(y))
);
				if (dist > outline_width) continue;

				vec2 offset = vec2(float(x), float(y)) * pixel;
				max_alpha = max(
					max_alpha,
					texture(TEXTURE, UV + offset).a
				);
			}
		}

		outline = max_alpha * (1.0 - alpha);
	}

	// ------------------------------------------------------------------------
	// GLOW
	// ------------------------------------------------------------------------
	float glow = 0.0;

	if (active && glow_size > 0.0 && alpha < 1.0) {
		int radius = int(ceil(glow_size));
		float weight_sum = 0.0;

		for (int x = -radius; x <= radius; x++) {
			for (int y = -radius; y <= radius; y++) {
				float dist = length(vec2(float(x), float(y)));
				if (dist > glow_size || dist <= outline_width) continue;

				vec2 offset = vec2(float(x), float(y)) * pixel;
				float a = texture(TEXTURE, UV + offset).a;

				float weight = exp(
					- (dist * dist) / (glow_size * glow_size * 0.5)
				);

				glow += a * weight;
				weight_sum += weight;
			}
		}

		if (weight_sum > 0.0) {
			glow = (glow / weight_sum) * glow_intensity * (1.0 - alpha);
		}
	}

	// ------------------------------------------------------------------------
	// SCREEN-SPACE CHROMATIC DISTORTION
	// ------------------------------------------------------------------------
	vec4 screen_color = vec4(0.0);

	if (active && enable_screen_space && distortion_strength > 0.0 && alpha > 0.0) {
		vec2 center = vec2(0.5);
		vec2 to_center = SCREEN_UV - center;
		float dist = length(to_center);

		vec2 distortion = to_center * distortion_strength * dist;

		vec2 uv_r = SCREEN_UV - distortion - to_center * chromatic_strength;
		vec2 uv_g = SCREEN_UV - distortion;
		vec2 uv_b = SCREEN_UV - distortion + to_center * chromatic_strength;

		screen_color = vec4(
			texture(screen_texture, uv_r).r,
			texture(screen_texture, uv_g).g,
			texture(screen_texture, uv_b).b,
			1.0
		);
	}

	// ------------------------------------------------------------------------
	// COMPOSITION
	// ------------------------------------------------------------------------

	// Screen-space blend
	if (active && enable_screen_space && distortion_strength > 0.0 && alpha > 0.0) {
		color.rgb = mix(
			color.rgb,
			screen_color.rgb,
			alpha * intensity
		);
	}

	// Outline blend
	if (outline > 0.0) {
		float amt = outline * intensity * outline_color.a;
		color.rgb = mix(color.rgb, outline_color.rgb, amt);
		color.a = max(color.a, amt);
	}

	// Glow blend
	if (glow > 0.0) {
		float amt = glow * intensity * glow_color.a;
		color.rgb = mix(color.rgb, glow_color.rgb, amt);
		color.a = max(color.a, amt);
	}

	COLOR = color;
}
Live Preview
Tags
chromatic aberration, glow, outline
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from TTien63

Related shaders

guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments