Progress Bar Anything

Add a simple progress display within canvas items.

This is a very simple shader that was intended to be used to make hybrid Button + Progress Bars. It fills left to right and allows you to define a fill color, min/max and current values.

This was inspired by the idle game “Your Chronicle” which used buttons that doubled as progress bars and I wanted a similar effect.

Shader code
shader_type canvas_item;

uniform vec4 fill_color: source_color = vec4(1.0);
uniform float min_val;
uniform float current_val;
uniform float max_val;

/*
	linear normalization from one range to another
*/
float linear(float old_min, float old_max, float new_min, float new_max, float current) {
	float old_range = (old_max - old_min);
	float new_range = (new_max - new_min);
	float new_val = (((current - old_min) * new_range) / old_range ) + new_min;
	return new_val;
}

void fragment() {
	vec4 color = COLOR;
	
	float normalized = linear(min_val, max_val, 0.0, 1.0, current_val);
	
	/*
	If the fragcoord.x is less than or equal to the calculated point
	between min and max that current_value falls on, mix with our
	fill color
	*/
	if (UV.x <= normalized) {
		color.rgb = mix(color.rgb, fill_color.rgb, 0.5);
	}
	
	COLOR = color;
}
Tags
progress, progress bar
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.

Related shaders

Dynamic Progress Bar with Waves and Particles

Procedural Segmented Progress Bar

Simple Radial Progress Bar

Subscribe
Notify of
guest

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
RcubDev
RcubDev
10 months ago

Great shader!

I ended up needing to have the color fill vertically instead of horizontally. Here is the shader code:

Vertical Shader

Added fill_from_top boolean – When true the progress will fill starting from the top of the sprite the shader is attached to. When false the progress will fill starting from the bottom of the sprite that the shader is attached toshader_type canvas_item;

uniform vec4 fill_color: source_color = vec4(1.0);
uniform float min_val;
uniform float current_val;
uniform float max_val;
uniform bool fill_from_top = true;

/*
linear normalization from one range to another
*/
float linear(float old_min, float old_max, float new_min, float new_max, float current) {
float old_range = (old_max – old_min);
float new_range = (new_max – new_min);
float new_val = (((current – old_min) * new_range) / old_range ) + new_min;
return new_val;
}

void fragment() {
vec4 color = COLOR;

if(fill_from_top) {
float normalized = linear(min_val, max_val, 0.0, 1.0, current_val);
if (UV.y <= normalized) {
color.rgb = mix(color.rgb, fill_color.rgb, 0.5);
}
}
else {
float real_current_val = max_val – current_val;
float normalized = linear(min_val, max_val, 0.0, 1.0, real_current_val);
if (UV.y >= normalized) {
color.rgb = mix(color.rgb, fill_color.rgb, 0.5);
}
}

COLOR = color;
}

Horizontal Shader (basically this same shader but with some extra flexibility):

Added fill_from_left boolean – When true the progress will fill starting from the left of the sprite the shader is attached to (how this shader was originally made). When false the progress will fill starting from the right of the sprite the shader is attached toshader_type canvas_item;

uniform vec4 fill_color: source_color = vec4(1.0);
uniform float min_val;
uniform float current_val;
uniform float max_val;
uniform bool fill_from_left = true;

/*
linear normalization from one range to another
*/
float linear(float old_min, float old_max, float new_min, float new_max, float current) {
float old_range = (old_max – old_min);
float new_range = (new_max – new_min);
float new_val = (((current – old_min) * new_range) / old_range ) + new_min;
return new_val;
}

void fragment() {
vec4 color = COLOR;

if(fill_from_left) {
float normalized = linear(min_val, max_val, 0.0, 1.0, current_val);
if (UV.x <= normalized) {
color.rgb = mix(color.rgb, fill_color.rgb, 0.5);
}
}
else {
float real_current_val = max_val – current_val;
float normalized = linear(min_val, max_val, 0.0, 1.0, real_current_val);
if (UV.x >= normalized) {
color.rgb = mix(color.rgb, fill_color.rgb, 0.5);
}
}
COLOR = color;
}

If I get more time I might just post these as standalone shaders on this site and link back to here. Thanks for the starting point!

Last edited 10 months ago by RcubDev