h1

Photoshop math with GLSL shaders

January 5, 2009

I usualy play with Photoshop to try post-processing effects on photos or game screenshots, it’s a lot faster than coding directly anything in shaders, but at the end I wanted to see my effects running in real-time. So I adapted a big part of the C-like code from this famous Photoshop blending mode math page + missing blending modes to GLSL (and now HLSL!) code and I added a few other useful things from Photoshop, such as Hue/Saturation/Luminance conversion, desaturation, contrast.

For example, I tried combining a few things in my Editor:

photoshopmath_tn

photoshopmath_editor_tn

Translating Photoshop operations on layers gives this kind of code:

uniform sampler2D Tex;
uniform sampler1D GradientMap;
uniform sampler1D GradientGround;

varying vec2 uv;

void main()
{

vec3 color = texture2D(Tex, uv).xyz;

// Split-tone
vec4 colorDesat = Desaturate(color, 1.0);
vec3 splitColor = texture1D(GradientMap, colorDesat.r).rgb;
vec3 pass1 = BlendColor(color, splitColor);

// Vertical gradient
vec4 verticalGradientColor = texture1D(GradientGround, uv.y);
vec3 pass2 = mix(pass1, BlendColor(pass1, verticalGradientColor.rgb), verticalGradientColor.a);

// Luminosity
vec3 pass3 = mix(pass2, BlendLuminosity(pass2, color + vec3(0.08)), 0.5);

// Linear light at 40%
vec3 pass4 = mix(pass3, BlendLinearLight(pass3, color), 0.4);

// Final
gl_FragColor = vec4(pass4, 1.0);

}

Here is the list of blending modes and functions I got:

Blending modes:

  • Normal
  • Lighten
  • Darken
  • Multiply
  • Average
  • Add
  • Substract
  • Difference
  • Negation
  • Exclusion
  • Screen
  • Overlay
  • SoftLight
  • HardLight
  • ColorDodge
  • ColorBurn
  • LinearDodge
  • LinearBurn
  • LinearLight
  • VividLight
  • PinLight
  • HardMix
  • Reflect
  • Glow
  • Phoenix
  • Hue
  • Saturation
  • Color
  • Luminosity

Functions:

  • Desaturation
  • RGBToHSL (RGB to Hue/Saturation/Luminance)
  • HSLToRGB (Hue/Saturation/Luminance to RGB)
  • Contrast

Here is my GLSL code, almost all the blending modes are macros and some do per-channel operation so it could run faster using vector operations with masks (to take into account the values per component), but still I guess it could help :)

Download PhotoshopMath.glsl

Update:

Oh and by the way you noticed the Split-Tone pass in my example:

// Split-tone
vec4 colorDesat = Desaturate(color, 1.0);
vec3 splitColor = texture1D(GradientMap, colorDesat.r).rgb;
vec3 result = BlendColor(color, splitColor);

It’s just the same thing than the Gradient Map… of the Create new fill or adjustment layer in Photoshop but blended in Color mode, which reminds me Color Temperature and Cross-Processing effects :)

Update:

I updated the .glsl file, because I forgot a line in the ContrastSaturationBrightness() function and I had some issues on specific hardware due to conditional returns, so now it’s fixed.

And now, here is the HLSL version :)

Download PhotoshopMath.hlsl

17 comments

  1. Great post! Implemented many of the photoshop blend modes in HLSL myself a while ago, but not as many as you’ve done here.


  2. [...] I added the code in my Photoshop Math (GLSL/HLSL) shaders. [...]


  3. Hello, and thanks for your webside and sorry my bad english.

    I will like to know how I change in the fragment shader the HUE of a texture . and not modify the saturation and brithness

    Thanks for all


  4. You can use my code and do:
    vec3 hsl = RGBToHSL(texture2d(yourTexture, yourUV).rgb);
    hsl.x = whateverYouWant; // .x is hue, .y is saturation, .z is brightness
    vec3 color = HSLToRGB(hsl);


  5. Thanks very much.Works well.
    I will see you web page to knews comments.

    Thanks for all.


  6. Hi, I’ve been beating my brains out trying to blend two layers in case both are semitransparent. My results slightly differ from photoshop.

    I write this shader (for ex., for difference):

    // Performs “over”-type blending (colors must be premultiplied by alpha)
    float4 blend(float4 overlying, float4 underlying)
    {
    float3 blended = overlying.rgb + ((1-overlying.a)*underlying.rgb);
    float alpha = underlying.a + (1-underlying.a)*overlying.a;
    return float4(blended, alpha);
    }

    // Performs advanced “over”-type blending
    // (newcolor is the result of advanced blending (for ex, overlying.rgb * underlying.rgb))
    float4 blend(float3 newcolor, float4 overlying, float4 underlying)
    {
    // Here the problem! (I think…)
    float alpha = overlying.a * underlying.a;
    float3 blended = newcolor*alpha + ((1-alpha)*overlying.rgb);

    return blend(float4(blended, overlying.a), underlying);
    }

    // Input images
    sampler2D underlyingSampler : register(s0);
    sampler2D overlyingSampler : register(s1);
    // Opacity of source image
    float opacity : register(c0);

    float4 main(float2 uv : TEXCOORD) : COLOR
    {
    float4 overlying = tex2D(overlyingSampler, uv);
    float4 underlying = tex2D(underlyingSampler, uv);
    overlying.a *= opacity;
    overlying.rgb *= overlying.a;
    underlying.rgb *= underlying.a;

    float4 result = blend(abs(overlying.rgb – underlying.rgb), overlying, underlying);
    return float4(result.rgb/result.a, result.a);
    }


  7. [...] wasn’t working. I tried to figure out what was wrong with it, but eventually I ran into another post that had a different way of expressing the math … it was in HLSL! Plugging in this HLSL worked! [...]


  8. I don’t know If I said it already but …This blog rocks! I gotta say, that I read a lot of blogs on a daily basis and for the most part, people lack substance but, I just wanted to make a quick comment to say I’m glad I found your blog. Thanks, :)

    A definite great read….


  9. In photoshop there are some additional bleding mode that missing here
    1) Darker Color:
    Get the color of color with min luminosity
    float bgLuminosity = 0.3 * bg.Red + 0.59 * bg.Green + 0.11 * bg.Blue;
    float fgLuminosity = 0.3 * fg.Red + 0.59 * fg.Green + 0.11 * fg.Blue;
    if (bgLuminosity fgLuminosity)
    result = bg;
    else
    result = fg;
    2) The second mode is Lighter color:
    Get the color of color with max luminosity
    float bgLuminosity = 0.3 * bg.Red + 0.59 * bg.Green + 0.11 * bg.Blue;
    float fgLuminosity = 0.3 * fg.Red + 0.59 * fg.Green + 0.11 * fg.Blue;
    if (bgLuminosity > fgLuminosity)
    result = bg;
    else
    result = fg;


  10. Just a question.. what license are you releasing this under ?
    Can it be used in a commercial project free of charge ?


  11. The Luminance blend mode seems incorrect, at least if Photoshop CS4 is any sort of reference.

    Photoshop’s Luminance is calculated as:

    Luminance = 0.30R + 0.59G + 0.11B

    You can verify this by placing any image in Luminance blend mode over any grayscale image. Compare the result to taking the original image and using the Channel Mixer (monochrome output) with settings 30, 59 and 11.

    HSL Lightness (L) is calculated as:

    L = (max(R, G, B) – min(R, G, B)) / 2

    This is from your own algorithms. The two formulas are clearly not the same.

    Using U for Luminance, I don’t know what the RGB2HSU formula is (although I could guess) nor what the HSU2RGB formula is. I would love to find out.


  12. [...] Photoshop effects Possibly related posts: (automatically generated)Scenarios for jscJSC Ultra Application to Support [...]


  13. Thanks for this excellent post.
    Will soon be integrated in my soft !


  14. With the HLSL I was able to achieve significantly better performance when using these blending functions without the ‘if’ conditionals. Instead I use the following macro to create an interpolation factor for a lerp:

    #define HardFactor(factor) max(sign(##factor – 0.5), 0)

    That macro results in 0 for values less than 0.5 or 1 for values greater than 0.5. Nice little trick to keep around as long as you use it wisely.


  15. Any chance in these being ported to GLSL ES2?


  16. Xn had a very good question… Can this be used in a commercial application?
    BTW, great blog, very helpful.
    Thanks!


  17. You can use it for a commercial application.
    If you have credits in your game/app, you can have a special thanks for me (Romain Dura), or if you have a blog with screenshots or whatever you can link to this page when you post something using my code.
    And you can reply here with a link to your application/website so we can see what you guys did.
    Whatever you think is right.



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.