9
\$\begingroup\$

I have a texture and I want to "colorize" it with a given color, lets say cyan (#00ffff) or purple (#800080). What I want to do, is get all the pixel values from the texture, and remove the color and keep the "brightness" and "saturation" and apply to the desired color. There is a tool in GIMP to do this called Colorize (Colors -> Colorize.. while editing), I made an example below.

This is will all be done in a shader (GLSL), although this is probably a general algorithm.

enter image description here

\$\endgroup\$
7
\$\begingroup\$

This live demo uses the algorithm described by @Dries in a fragment shader to colorize the incoming fragment color.

It basically can be summarised by:

vec4 getInputColor()
{
    TODO: sample from a texture / use a uniform var / varying ...
}

vec4 toGrayscale(in vec4 color)
{
  float average = (color.r + color.g + color.b) / 3.0;
  return vec4(average, average, average, 1.0);
}

vec4 colorize(in vec4 grayscale, in vec4 color)
{
    return (grayscale * color);
}

void main()
{
  // This is the color you want to apply
  // in the "colorize" step. Should ultimately be a uniform var.
  vec4 c = vec4(0.0, 1.0, 1.0, 1.0);

  // The input fragment color.
  // Can come from a texture, a varying or a constant.
  vec4 inputColor = getInputColor();

  // Convert to grayscale first:
  vec4 grayscale = toGrayscale(inputColor);

  // Then "colorize" by simply multiplying the grayscale
  // with the desired color.
  vec4 colorizedOutput = colorize(grayscale, c);

  // Done!
  gl_FragColor = colorizedOutput;
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ This would be better as a comment, since without the link, it's a very vague answer. \$\endgroup\$ – MichaelHouse May 31 '14 at 22:04
  • \$\begingroup\$ @Byte56 Without the link? \$\endgroup\$ – glampert May 31 '14 at 22:06
  • 2
    \$\begingroup\$ Yes. In the future, if the link were to no longer point to anything, this answer would be no good. \$\endgroup\$ – MichaelHouse May 31 '14 at 22:44
  • \$\begingroup\$ Why, the only way it can happen is if the website goes offline, and this can happen even with Wikipedia. I'm pretty sure the shader source is encoded in the URL, so it doesn't have an expiration date. But to be sure, I'll post a code snippet here then... \$\endgroup\$ – glampert May 31 '14 at 23:20
  • 2
    \$\begingroup\$ Note that this answer will not produce the results as shown in the question. White will also become the color you want to colorize with, but in the question white is preserved. My guess is that the correct answer would have to convert the color to hsl and then only adjust the hue, but leave saturation and lightness intact. \$\endgroup\$ – Adion Feb 12 '17 at 8:29
2
\$\begingroup\$

If you want it exactly like the thing above you'll first have to make your render grayscale.

You can do this in the pixel shader by taking a sample of the texture you were using (or only the color doesn't matter). Make a sum of the r, g and b value and divide that by 3. Return that value as the rgb values from the pixel shader.

Then do this (with or without the grayscale). Multiply the color of your texture (or the value of the grayscale) with the color you want.

Return that value in your pixel shader and that should be it.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

If you just multiply a grayscale with a color, white will become that color instead of staying white. Instead, multiply only for gray values up to 0.5 and for brighter values, mix the color with white (the mix value will be the gray value normalized from [0.5..1.0] to [0.0..1.0]. So the mix amount would be (grayVal-0.5)*2.0

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Adding (pseudo) code and/or name (if exist) of mix algorithm would greatly improve the answer. \$\endgroup\$ – wondra Apr 19 '18 at 19:25
  • \$\begingroup\$ @wondra "mix" is the name OpenGL uses for "lerp" so mix(a, b, t) = (1 - t) * a + t * b \$\endgroup\$ – DMGregory Apr 19 '18 at 19:53
0
\$\begingroup\$

I had this same question. The following is one of the simpler solutions I tried today while looking to colourise an image while cleanly preserving black and white values. It has complications and limitations to be sure (see discussion below), but it should be pretty usable.

If you're ok with the additional computations of HSL conversion, skip my hacky RGB approach and just check out this article on Photoshop blending modes; the GIMP equivalent you're looking for is Photoshop's "Color" mode.

If you want something a little more minimal (no colour mode conversions), hopefully this helps...

Steps:

  1. Convert input image values to greyscale using perceptual brightness
  2. Find the average value of the desired colour and use this to centre the RGB values
  3. Modulate (in the artistic sense) the original input values using mathematical power

This method behaves reasonably well with the full range of colour input values, including black or white (mix/lerp methods do not). It also retains the brightness of the original image pretty successfully (multiplication methods do not). It is not particularly successful with high-saturation-low-value colours (like a saturated navy blue), since the RGB nature of the computations result in less saturated output. I've arbitrarily boosted saturation in the code example on ShaderToy, which of course has its own downsides (burning and hue shifts can result at higher levels). But this is all pretty simple, and hopefully fairly fast due to not relying on HSL/HSV conversions.

If you know your RGB input values will always be within a known brightness range, a mix/lerp solution might be more accurate and reliable. I needed something that was a little more flexible (if not strictly accurate for hue/saturation), so came up with this.

Pseudo-code:

float3 image = // input image values
float imageGrey = image.r * 0.3 + image.g * 0.7 + image.b * 0.1; // roughly perceptual greyscale conversion

float3 color = // input colour values
float colorAverage = (color.r+color.g+color.b)*0.3334; // mid-point of the float3
float3 colorPow = 1.0+colorAverage-color; // centre at 1.0 and invert the colour values

return pow(imageGrey, colorPow);

Real example:

https://www.shadertoy.com/view/wsVyRG

References: (for the perceptual greyscale conversion)

https://gist.github.com/Volcanoscar/4a9500d240497d3c0228f663593d167a https://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.