OpenGL GLSL: How to implement the concept of gradient map in photoshop using fragment shader?

3.6k views Asked by At

This may be not the perfect question for stackoverflow but I have asked this in graphicdesign network of stackexchange but did not get any answer so posting it here. I am trying to implement gradient map of Photoshop programmatically using glsl fragment shader. Consider below shown gradient map where all white components are replaced by green and black by blue. For other color components Photoshop calculates based on the linear gradient between blue and green. I know there is a math behind this calculation. Does any one know formula to find output color for particular input color value?

enter image description here

2

There are 2 answers

5
Reto Koradi On BEST ANSWER

Based on the image and description, it looks like this maps the original grayscale value of the image to the blue-green gradient.

There are various ways of converting color to grayscale, depending on the color system used. Many of the simple one are just a weighted sum of the RGB components. For example a widely used formula for converting RGB to brightness (Y) is:

Y = 0.299 * R + 0.587 * G + 0.114 * B

See the Grayscale wikipedia page for more background on the topic and all the different options.

Then you can use a linear interpolation of blue and green with the grayscale value as the parameter. The math for this would be easy anyway, but GLSL even has a built-in mix() function for this purpose.

Some possible code fragments for GLSL, based on reading the original color from a texture Tex using texture coordinates TexCoord:

uniform sampler2D Tex;
in vec2 TexCoord;
out vec4 FragColor;
...
    vec4 origColor = texture(Tex, TexCoord);
    float grayscaleValue = dot(origColor.rgb, vec3(0.299, 0.587, 0.114));
    FragColor = mix(vec4(0.0, 0.0, 1.0, 1.0), vec4(0.0, 1.0, 0.0, 1.0), grayscaleValue);

In this code, vec3(0.299, 0.587, 0.114) contains the coefficients from the RGB to grayscale (brightness) formula above. The first two arguments of the mix() function are the start and end colors of the gradient:

  • blue: vec4(0.0, 0.0, 1.0, 1.0)
  • green: vec4(0.0, 1.0, 0.0, 1.0)

Note that a simple linear interpolation between the two colors in RGB space is not ideal, particularly if the colors are very different. You can make this more elaborate, and potentially get better quality, by operating in a different color space like HLS.

Another option to possibly improve the visual appearance is that you use more than two colors to specify the gradient. For example, you could assign blue to brightness 0.0, cyan to brightness 0.5, and green to brightness 1.0. It requires a little more logic than the GLSL code above, but it's just an extension of the same principle.

1
Fabien Quatravaux On

I tried different gradient maps in Photoshop, and my settings seams different from the ones of the OP. In my case, applying the gradient map filter to a black to white gradient image is showing that black remains black and white remains white. Every shade of grey between is replaced by some color between green and blue. If you map directly shades of grey to the linear gradient, blacks and whites will disappear. Photoshop gradient map seams to retain the Luminescence of the original colors.

Here is the formula I get to match Photoshop gradient map for my settings.

Given (Rg1,Gg1,Bg1) and (Rg2,Gg2,Bg2) the gradient values, (Ro,Go,Bo) the original color value and (Rr,Gr,Br) the resulting color.

  1. get the color to be converted in the HSL space :

(Ho, So, Lo) = rgb2hsl(Ro, Go, Bo)

  1. calculate the linear interpolation of the gradient (in RGB) for Lo value :

(Rt,Gt,Bt) = (Rg1+[Rg2-Rg1]*Lo, Gg1+[Gg2-Gg1]*Lo), Bg1+[Bg2-Bg1]*Lo)

  1. get this value in the HSL space :

(Ht,St,Lt) = rgb2hsl(Rt, Gt, Bt)

  1. Retain the L value from the original color

(Rr,Gr,Br) = hsl2rgb(Ht,St,Lo)

rgb2hsl and hsl2rgb code examples can be found here : HSL to RGB color conversion