GLSL to tint greyscale image to color

My goal is to create a GLSL shader that will allow tinting of a greyscale image from 1 color to another color, depending on the range of an input from 0-1.
I have read this part of the Book Of Shaders The Book of Shaders: color
and also this related OpenGL question on Stackoverflow iphone - OpenGL ES 1.1: How to change texture color without losing luminance? - Stack Overflow

but am still stumped.
Can anyone point me in the right direction or get me started?

@art3mis

Multiplication is an easy way to do this if you’re working with a gray scale image.
The first step for us to think about is the math that’s going to drive this operation.

Your first exploration here might be with multiplication. Thinking this way, the value of a given pixel will determine how strong the color comes through.

If our original pixel has gray value of (0.1, 0.1, 0.1, 1.0) we could just multiply that by our desired color (1.0, 0.0. 0.0, 1.0). The result would be (0.1, 0.0, 0.0, 1.0)… written another way:

(0.1, 0.1, 0.1, 1.0) x (1.0, 0.0. 0.0, 1.0) = (0.1, 0.0, 0.0, 1.0)

That’s a kind of tinting - maybe not totally what you’re after, but let’s move that to a shader so you can see it in action.

A movie file in TOP connected to a monochrome top will get you a gray scale image to start with. From there we can connect this out a glsl TOP:

Next let’s add a uniform to your shader that will be our tint color.

image

Next we can edit the shader code to start to get a result.

We need to start by declaring our new uniform:

// our new code is here
uniform vec3 uTintColor;

out vec4 fragColor;
void main()
{
	// vec4 color = texture(sTD2DInputs[0], vUV.st);
	vec4 color = vec4(1.0);
	fragColor = TDOutputSwizzle(color);
}

Next we need to change how our shader is working. We want to use the incoming color of our texture, so we’ll un-comment the first line inside of main() so we have a gray scale value to reference. We’ll also get rid of the second color declaration:

// Example Pixel Shader

// uniform float exampleUniform;

// our new code is here
uniform vec3 uTintColor1;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	fragColor = TDOutputSwizzle(color);
}

What you should have now is this:

To implement the our first example let’s make a few changes to our shader:

// Example Pixel Shader

// uniform float exampleUniform;

// our new code is here
uniform vec3 uTintColor1;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	color.rgb *= uTintColor1;
	fragColor = TDOutputSwizzle(color);
}

All we’ve done so far is multiply our pixel’s value by that new uniform. What you should now have is:

Let’s now add a second uniform that’s our second color:
image

We should add this uniform to our shader as well:

// our new code is here
uniform vec3 uTintColor1;
uniform vec3 uTintColor2;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	color.rgb *= uTintColor1;
	fragColor = TDOutputSwizzle(color);
}

Now we have a problem… we could do something similar to our first step, but that won’t let us mix between these colors. Instead, let’s declare two new variables that will hold our color information for us:

// our new code is here
uniform vec3 uTintColor1;
uniform vec3 uTintColor2;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	vec3 col1 	= color.rgb * uTintColor1;
	vec3 col2 	= color.rgb * uTintColor2;
	color.rgb *= uTintColor1;
	fragColor = TDOutputSwizzle(color);
}

This is great, but we still need a way to mix between those two colors. Let’s add another uniform:

image

And add this to our shader:

// our new code is here
uniform vec3 uTintColor1;
uniform vec3 uTintColor2;
uniform float uMix;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	vec3 col1 	= color.rgb * uTintColor1;
	vec3 col2 	= color.rgb * uTintColor2;
	color.rgb *= uTintColor1;
	fragColor = TDOutputSwizzle(color);
}

Finally, let’s use this new variable to mix between color1 and color2:

// our new code is here
uniform vec3 uTintColor1;
uniform vec3 uTintColor2;
uniform float uMix;

out vec4 fragColor;
void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	vec3 col1 	= color.rgb * uTintColor1;
	vec3 col2 	= color.rgb * uTintColor2;
	color.rgb = mix(col1, col2, uMix);
	fragColor = TDOutputSwizzle(color);
}

We’re now mixing between these two tints based on the value of the uniform uMix:

Animated that should look like this:
mixGIF

Is that enough to get you moving? GLSL is a strange animal to get a handle of - the more you experiment the more it will make sense.

1 Like

That’s awesome Matthew. Very much appreciated.