Color Adjustments

Hello! I researched and compiled many resources to create this shader. 

Shader code
shader_type canvas_item;

group_uniforms R_G_B;
// Uses a calculation for saturation.
uniform float dot_saturation : hint_range(-1.0, 1.0, 0.1) = 0.0;
// Averages the RGB values for saturation.
uniform float avg_saturation : hint_range(-1.0, 1.0, 0.1) = 0.0;
// Mixes the texture color with white or black.
uniform float luminance : hint_range(-1.0, 1.0, 0.1) = 0.0;

// Directly modify the RGB values.
group_uniforms R_G_B.Red;
uniform float mix_red : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float add_red : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_red : hint_range(-1.0, 1.0, 0.1) = 0.0;

group_uniforms R_G_B.Green;
uniform float mix_green : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float add_green : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_green : hint_range(-1.0, 1.0, 0.1) = 0.0;

group_uniforms R_G_B.Blue;
uniform float mix_blue : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float add_blue : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_blue : hint_range(-1.0, 1.0, 0.1) = 0.0;

// Allows the user to adjust color in the HSV color space.
group_uniforms H_S_V;
uniform float hue_hsv : hint_range(-1.0, 1.0, 0.1) = 0.0;

group_uniforms H_S_V.Saturation;
uniform float add_saturation_hsv : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_saturation_hsv : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float scale_saturation_hsv : hint_range(1.0, 10.0, 0.1) = 1.0;

group_uniforms H_S_V.Value;
uniform float add_value : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_value : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float scale_value : hint_range(1.0, 10.0, 0.1) = 1.0;

// Allows the user to adjust color in the HSV color space.
group_uniforms H_S_L;
uniform float hue_hsl : hint_range(-1.0, 1.0, 0.1) = 0.0;

group_uniforms H_S_L.Saturation;
uniform float add_saturation_hsl : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_saturation_hsl : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float scale_saturation_hsl : hint_range(1.0, 10.0, 0.1) = 1.0;

group_uniforms H_S_L.Lightness;
uniform float add_lightness : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float multiply_lightness : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float scale_lightness : hint_range(1.0, 10.0, 0.1) = 1.0;

group_uniforms Tint; 
// Allows the user to pick a tint color, a strength (weight), and blend type.
uniform vec3 tint : source_color;
uniform float tint_strength : hint_range(0.0, 1.0) = 0.0;
uniform int tint_mode : hint_enum("Mix", "Multiply", "Divide", "Add", "Subtract") = 0;
group_uniforms;

group_uniforms Contrast;
// Modify other settings such as brightness, contrast, and invert.
uniform float brightness : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float contrast : hint_range(-1.0, 1.0, 0.1) = 0.0;
uniform float scale_contrast : hint_range(1.0, 10.0, 0.1) = 1.0;
uniform bool invert = false;

// Converts an RGB color to HSV.
vec3 rgb_to_hsv(vec3 c) 
{
	vec4 k = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
	vec4 p = mix(vec4(c.bg, k.wz), vec4(c.gb, k.xy), step(c.b, c.g));
	vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
	float d = q.x - min(q.w, q.y);
	float e = 1.0e-10;
	return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

// Converts an HSV color back to RGB.
vec3 hsv_to_rgb(vec3 c) 
{
	vec4 k = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
	vec3 p = abs(fract(c.xxx + k.xyz) * 6.0 - k.www);
	return c.z * mix(k.xxx, clamp(p - k.xxx, 0.0, 1.0), c.y);
}

// Converts an HSV color to HSL.
vec3 hsv_to_hsl(vec3 c) 
{
	float l = 0.5 * c.z * (2.0 - c.y);
	float s = l > 0.0 && l < 1.0 ? c.z * c.y / (1.0 - abs(2.0 * l - 1.0)) : 0.0;
	return vec3(c.x, s, l);
}

// Converts an HSL color to HSV.
vec3 hsl_to_hsv(vec3 c) 
{
	float v = (2.0 * c.z + c.y * (1.0 - abs(2.0 * c.z - 1.0))) / 2.0;
	float s = 2.0 * (v - c.z) / v;
	return vec3(c.x, s, v);
}

// Converts an RGB color to HSL.
vec3 rgb_to_hsl(vec3 c) 
{
	float vmax = max(c.r, max(c.g, c.b));
	float vmin = min(c.r, min(c.g, c.b));
	float h, s, l = (vmax + vmin) / 2.0;
	float d = vmax - vmin;
	if (d < 0.00001) { return vec3(0.0, 0.0, l); }
	s = l > 0.5 ? d / (2.0 - vmax - vmin) : d / (vmax + vmin);
	if (vmax == c.r) { h = (c.g - c.b) / d + (c.g < c.b ? 6.0 : 0.0); }
	if (vmax == c.g) { h = (c.b - c.r) / d + 2.0; }
	if (vmax == c.b) { h = (c.r - c.g) / d + 4.0; }
	h /= 6.0;
	return vec3(h, s, l);
}

// Psudeo-private helper function for HSL to RBG function.
float _hsl_hue_to_rgb(float p, float q, float t) 
{
	if (t < 0.0) t += 1.0;
	if (t > 1.0) t -= 1.0;
	if (t < 1.0 / 6.0) { return p + (q - p) * 6.0 * t; }
	if (t < 1.0 / 2.0) { return q; }
	if (t < 2.0 / 3.0) { return p + (q - p) * (2.0 / 3.0 - t) * 6.0; }
	return p;
}

// Converts and HSL color to RGB.
vec3 hsl_to_rgb(vec3 c) 
{
	float r, g, b;
	if (c.y < 0.00001) { r = g = b = c.z; }
	else {
		float q = c.z < 0.5 ? c.z * (1.0 + c.y) : c.z + c.y - c.z * c.y;
		float p = 2.0 * c.z - q;
		r = _hsl_hue_to_rgb(p, q, c.x + 1.0 / 3.0);
		g = _hsl_hue_to_rgb(p, q, c.x);
		b = _hsl_hue_to_rgb(p, q, c.x - 1.0 / 3.0); }
	return vec3(r, g, b);
}

// Blend a color between three values in the range of -1.0 to 1.0. 
vec3 _blend_rgb(vec3 input_color, vec3 min_color, vec3 max_color, float time) 
{
	// An easy way to switch 0s and 1s mathematically is to use the expression 1 - x.
	//vec3 min_color = vec3(1.0 - max_color.x, 1.0 - max_color.y, 1.0 - max_color.z);
	return time < 0.0 ? mix(input_color, min_color, abs(time)) : mix(input_color, max_color, time);
}

// Modify the texture's saturation.
vec3 saturate_dot(vec3 input_color) 
{
	vec3 color_dot = vec3(dot(input_color, vec3(0.299, 0.587, 0.114)));
	vec3 max_color = any(isnan(input_color / color_dot)) ? vec3(1.0, 1.0, 1.0) : input_color / color_dot;
	return _blend_rgb(input_color, color_dot, max_color, dot_saturation);
}

// Modify the texture's saturation using the average RGB value. Produces a result with less depth.
vec3 saturate_avg(vec3 input_color)
{
	float rgb_average = (input_color.r + input_color.g + input_color.b) / 3.0;
	vec3 color_avg = vec3(rgb_average, rgb_average, rgb_average);
	vec3 max_color = any(isnan(input_color / color_avg)) ? vec3(1.0, 1.0, 1.0) : input_color / color_avg;
	return _blend_rgb(input_color, color_avg, max_color, avg_saturation);
}

// Mix black or white to change luminance. This lightens the color without affecting hue.
vec3 adjust_luminance(vec3 input_color) 
{
	return _blend_rgb(input_color, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0), luminance);
}

// Experiment with different methods to modify RGB values.
vec3 adjust_rgb(vec3 input_color)
{
	// Update red value.
	input_color = _blend_rgb(input_color, vec3(0.0, 1.0, 1.0), vec3(1.0, 0.0, 0.0), mix_red);
	input_color.r += add_red;
	input_color.r *= 1.0 + multiply_red;
	
	// Update green value.
	input_color = _blend_rgb(input_color, vec3(1.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), mix_green);
	input_color.g += add_green;
	input_color.g *= 1.0 + multiply_green;
	
	// Update blue value.
	input_color = _blend_rgb(input_color, vec3(1.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0), mix_blue);
	input_color.b += add_blue;
	input_color.b *= 1.0 + multiply_blue;
	
	return input_color;
}

// Performs all RGB color updates in one method; easier, but there is less control of method order.
vec3 shift_color_rgb(vec3 input_color) 
{
	return adjust_rgb(adjust_luminance(saturate_avg(saturate_dot(input_color))));
}

// Wrap the hue around it's orginal hue.
vec3 shift_hue_hsv(vec3 input_color) 
{
	return vec3(input_color.x + mod(hue_hsv, 1.0), input_color.y, input_color.z);
}

// Modifies increase, decrease, multiply, and scale the saturation.
vec3 adjust_saturation_hsv(vec3 input_color)
{
	vec3 adjusted_color = input_color;
	float new_saturation = (input_color.y + add_saturation_hsv) * (1.0 + multiply_saturation_hsv);
	//float new_saturation = input_color.y * (1.0 + multiply_saturation_hsv) + add_saturation_hsv;
	adjusted_color.y = clamp(new_saturation, 0.0, 1.0) * scale_saturation_hsv;
	return adjusted_color;
}

// Modifies increase, decrease, multiply, and scale the value.
vec3 adjust_value(vec3 input_color)
{
	vec3 adjusted_color = input_color;
	float new_value = (input_color.z + add_value) * (1.0 + multiply_value);
	//float new_value = input_color.z * (1.0 + multiply_value) + add_value;
	adjusted_color.z = clamp(new_value, 0.0, 1.0) * scale_value;
	return adjusted_color;
}

// Performs all color operations in a single method.
vec3 shift_color_hsv(vec3 input_color)
{
	return adjust_value(adjust_saturation_hsv(shift_hue_hsv(input_color)));
}

// Wrap the hue around it's orginal hue.
vec3 shift_hue_hsl(vec3 input_color) 
{
	return vec3(input_color.x + mod(hue_hsl, 1.0), input_color.y, input_color.z);
}

// Modifies increase, decrease, multiply, and scale the saturation.
vec3 adjust_saturation_hsl(vec3 input_color)
{
	vec3 adjusted_color = input_color;
	float new_saturation = (input_color.y + add_saturation_hsl) * (1.0 + multiply_saturation_hsl);
	//float new_saturation = input_color.y * (1.0 + multiply_saturation_hsl) + add_saturation_hsl;
	adjusted_color.y = clamp(new_saturation, 0.0, 1.0) * scale_saturation_hsl;
	return adjusted_color;
}

// Modifies increase, decrease, multiply, and scale the lightness.
vec3 adjust_lightness(vec3 input_color)
{
	vec3 adjusted_color = input_color;
	float new_lightness = (input_color.z + add_lightness) * (1.0 + multiply_lightness);
	//float new_lightness = input_color.z * (1.0 + multiply_lightness) + add_lightness;
	adjusted_color.z = clamp(new_lightness, 0.0, 1.0) * scale_lightness;
	return adjusted_color;
}

vec3 shift_color_hsl(vec3 input_color)
{
	return adjust_lightness(adjust_saturation_hsl(shift_hue_hsl(input_color)));
}

// Uses the various tint modes to apply blending effects.
vec3 tint_color (vec3 input_color_rgb) 
{
	// Mixes the colors together smoothly, resulting in a solid tint colored image at 1.0.
	if (tint_mode == 0) { return mix(input_color_rgb, tint, tint_strength); }
	
	// Multiplies the colors together, retaining visual contrast while darkening the base color.
	if (tint_mode == 1) { return input_color_rgb * mix(vec3(1.0, 1.0, 1.0), tint, tint_strength); }
	
	// Divides the colors, retaining visual contrast but negating the selected color and lightening.
	if (tint_mode == 2) { return input_color_rgb / max(vec3(0.00001, 0.00001, 0.00001), mix(vec3(1.0, 1.0, 1.0), tint, tint_strength)); }
	
	// Adds the colors together, creating a lightening effect (colors move toward white).
	if (tint_mode == 3) { return input_color_rgb + tint * tint_strength; }
	
	// Subtracts the colors, negating the selected color and creating a darkening effect (colors move toward black).
	if (tint_mode == 4) { return input_color_rgb - tint * tint_strength; }
	
	// Default case return the input color.
	return input_color_rgb;
}

void fragment() {
	// Get the color from our sprite texture.
	vec4 input_color = texture(TEXTURE, UV);
	
	// Drop the alpha value so modulate transparency effects still work on the sprite.
	vec3 color_rgb = input_color.rgb;
	
	
	// Updates the color in the HSV color space.
	vec3 color_hsv = rgb_to_hsv(color_rgb);
	color_hsv = shift_color_hsv(color_hsv);
	color_rgb = hsv_to_rgb(color_hsv);
	
	// Updates the color in the HSL color space.
	vec3 color_hsl = rgb_to_hsl(color_rgb);
	color_hsl = shift_color_hsl(color_hsl);
	color_rgb = hsl_to_rgb(color_hsl);
	
	// Updates the color in the RGB color space.
	// Note: this has to be done after the HSV and HSL updates or it will produce poor results.
	color_rgb = shift_color_rgb(color_rgb);
	
	// Tinting the color behaves the same as adding / mixing / multiplying RGB values.
	// Note: desaturation in the RGB color space produces significantly better grayscale depth.
	color_rgb = tint_color(color_rgb);
	
	// Changes brightness with a subtle effect on hue.
	color_rgb += vec3(1.0, 1.0, 1.0) * brightness;

	// Adjust color contrast around 0.5.
	if (!invert) { color_rgb = (color_rgb - 0.5) * (1.0 + contrast * scale_contrast) + 0.5; }
	
	// Use negative contrast to invert the color.
	if (invert) { color_rgb = (color_rgb - 0.5) * (1.0 + contrast * scale_contrast) * -1.0 + 0.5; }
	
	// Update the sprite texture color.
	COLOR.rgb = color_rgb;
}
Live Preview
Tags
brightness, Color, contrast, hsl, hsv, rgb, tint
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 pk4uf

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments