Animated and Gradient Outlines

It’s alot like hancan‘s (re)upload of the outline shader

However the first one has alternating thickness

Second one is a gradient between start_colour and end_color

Third one is both!

Most uniforms are pretty self explanatory, otherwise

block_size <- size of segmentation for line width variation in animated outline

 

shader_type canvas_item;

uniform float max_line_width = 10.0;
uniform float min_line_width = 5.0;
uniform float freq = 1.0;
uniform float block_size = 20.0;
uniform vec4 outline_colour = vec4(0,0,0,1);

const float pi = 3.1415;
const int ang_res = 16;


float hash(vec2 p, float s) {
	return fract(35.1 * sin(dot(vec3(112.3, 459.2, 753.2), vec3(p, s))));
}

float noise(vec2 p, float s) {
	vec2 d = vec2(0, 1);
	vec2 b = floor(p);
	vec2 f = fract(p);
	return mix(
		mix(hash(b + d.xx, s), hash(b + d.yx, s), f.x),
		mix(hash(b + d.xy, s), hash(b + d.yy, s), f.x), f.y);
}

float get_line_width(vec2 p, float s) {
	p /= block_size;
	float w = 0.0;
	float intensity = 1.0;
	for (int i = 0; i < 3; i++) {
		w = mix(w, noise(p, s), intensity);
		p /= 2.0;
		intensity /= 2.0;
	}
	
	return mix(max_line_width, min_line_width, w);
}

void fragment() {

	float alpha = 0.0;
	float timeStep = floor(TIME * freq);
	vec2 scale = TEXTURE_PIXEL_SIZE;
	scale *= get_line_width(UV / TEXTURE_PIXEL_SIZE, timeStep);
	for (int i = 0; i < ang_res; i++) {
		float angle = 2.0 * pi * float(i) / float(ang_res);
		vec2 disp = scale * vec2(cos(angle), sin(angle));
		alpha += texture(TEXTURE, UV + disp).a;
	}
	
	if ((alpha > 0.0) && (texture(TEXTURE, UV).a < 0.1)) {
		COLOR = outline_colour;
	}
	else {
		COLOR = texture(TEXTURE, UV);
	}
}
		if (pixelInRange(text, uv, curr * maxDist)) {
			hi = curr;
		}
		else {
			lo = curr;
		}
	}
	return hi;
	
}

void fragment() {
	vec2 scaledDist = TEXTURE_PIXEL_SIZE * line_width;
	float w = getClosestDistance(TEXTURE, UV, scaledDist);
	
	if (( w > 0.0) && (texture(TEXTURE, UV).a < 0.1)) {
		COLOR = mix(starting_colour, ending_colour, tanh(3.0*w));
	}
	else {
		COLOR = texture(TEXTURE, UV);
	}
	
}
Shader code
shader_type canvas_item;

uniform float max_line_width = 10.0;
uniform float min_line_width = 5.0;
uniform float freq = 1.0;
uniform float block_size = 20.0;
uniform vec4 starting_colour = vec4(0,0,0,1);
uniform vec4 ending_colour = vec4(1);

const float pi = 3.1415;
const int ang_res = 16;
const int grad_res = 8;

float hash(vec2 p, float s) {
	return fract(35.1 * sin(dot(vec3(112.3, 459.2, 753.2), vec3(p, s))));
}

float noise(vec2 p, float s) {
	vec2 d = vec2(0, 1);
	vec2 b = floor(p);
	vec2 f = fract(p);
	return mix(
		mix(hash(b + d.xx, s), hash(b + d.yx, s), f.x),
		mix(hash(b + d.xy, s), hash(b + d.yy, s), f.x), f.y);
}

float getLineWidth(vec2 p, float s) {
	p /= block_size;
	float w = 0.0;
	float intensity = 1.0;
	for (int i = 0; i < 3; i++) {
		w = mix(w, noise(p, s), intensity);
		p /= 2.0;
		intensity /= 2.0;
	}
	
	return mix(max_line_width, min_line_width, w);
}

bool pixelInRange(sampler2D text, vec2 uv, vec2 dist) {
	float alpha = 0.0;
	for (int i = 0; i < ang_res; i++) {
		float angle = 2.0 * pi * float(i) / float(ang_res);
		vec2 disp = dist * vec2(cos(angle), sin(angle));
		if (texture(text, uv + disp).a > 0.0) return true;
	}
	return false;
}

float getClosestDistance(sampler2D text, vec2 uv, vec2 maxDist) {
	if (!pixelInRange(text, uv, maxDist)) return -1.0;
	
	float hi = 1.0; float lo = 0.0;
	
	for (int i = 1; i <= grad_res; i++) {
		float curr = (hi + lo) / 2.0;
		if (pixelInRange(text, uv, curr * maxDist)) {
			hi = curr;
		}
		else {
			lo = curr;
		}
	}
	return hi;
	
}

void fragment() {
	float timeStep = floor(freq * TIME);
	vec2 scaledDist = TEXTURE_PIXEL_SIZE;
	scaledDist *= getLineWidth(UV / TEXTURE_PIXEL_SIZE, timeStep);
	float w = getClosestDistance(TEXTURE, UV, scaledDist);
	
	if (( w > 0.0) && (texture(TEXTURE, UV).a < 0.2)) {
		COLOR = mix(starting_colour, ending_colour, tanh(3.0*w));
	}
	else {
		COLOR = texture(TEXTURE, UV);
	}
	
}
Tags
ambience, animated, cool, effect, fire, glow, gradient, outline, video
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 9exa

Vortex overlay

Related shaders

Animated Noise and Sprite Mixer

2d sprite based vfx gradient shader

Moving gradient noise fog/ mist for Godot 4

Subscribe
Notify of
guest

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Torguen
Torguen
2 years ago

Thanks for the shader.
I think there is a mess with the published codes.
You say there are three shaders but in the center block there is only one piece of incomplete code.

furroy
furroy
1 year ago

cool effect, but glitches out in a couple seconds on my iphone 🙁

Calinou
14 days ago
Reply to  furroy

You need to decrease Time Rollover Secs in the Project Settings to avoid visible precision issues on mobile platforms, as mobile GPUs use lower shader precision by default. When targeting mobile, try using a value of 32 or lower (floating-point precision is halved at every power of two value).