Photoshop blending modes for the GLSLShader module

Thu Sep 18, 2014 2:11 am

EDIT: In Magic 1.6, the Photoshop blending modes are now included in the main installation (via the Blend module). I've left this tutorial here just in case anyone's curious or wants more information.
---

Here's a fun new tutorial courtesy of Romain Dura's blog: http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/.

This one's a bit more complicated than the Alpha Mask tutorial so you might want to try that one first (http://magicmusicvisuals.com/forums/viewtopic.php?f=3&t=146).

Ok, so the code below gives you MANY new awesome blending options for the GLSLShader module. All the code is directly from Romain's blog, except the little bit I added at the end so it would work in Magic.
Code: Select all
/*
** Copyright (c) 2012, Romain Dura romain@shazbits.com
**
** Permission to use, copy, modify, and/or distribute this software for any
** purpose with or without fee is hereby granted, provided that the above
** copyright notice and this permission notice appear in all copies.
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
** WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
** IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
** Photoshop & misc math
** Blending modes, RGB/HSL/Contrast/Desaturate, levels control
**
** Romain Dura | Romz
** Blog: http://mouaif.wordpress.com
** Post: http://mouaif.wordpress.com/?p=94
*/



/*
** Desaturation
*/

vec4 Desaturate(vec3 color, float Desaturation)
{
   vec3 grayXfer = vec3(0.3, 0.59, 0.11);
   vec3 gray = vec3(dot(grayXfer, color));
   return vec4(mix(color, gray, Desaturation), 1.0);
}


/*
** Hue, saturation, luminance
*/

vec3 RGBToHSL(vec3 color)
{
   vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)
   
   float fmin = min(min(color.r, color.g), color.b);    //Min. value of RGB
   float fmax = max(max(color.r, color.g), color.b);    //Max. value of RGB
   float delta = fmax - fmin;             //Delta RGB value

   hsl.z = (fmax + fmin) / 2.0; // Luminance

   if (delta == 0.0)      //This is a gray, no chroma...
   {
      hsl.x = 0.0;   // Hue
      hsl.y = 0.0;   // Saturation
   }
   else                                    //Chromatic data...
   {
      if (hsl.z < 0.5)
         hsl.y = delta / (fmax + fmin); // Saturation
      else
         hsl.y = delta / (2.0 - fmax - fmin); // Saturation
      
      float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
      float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
      float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;

      if (color.r == fmax )
         hsl.x = deltaB - deltaG; // Hue
      else if (color.g == fmax)
         hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue
      else if (color.b == fmax)
         hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue

      if (hsl.x < 0.0)
         hsl.x += 1.0; // Hue
      else if (hsl.x > 1.0)
         hsl.x -= 1.0; // Hue
   }

   return hsl;
}

float HueToRGB(float f1, float f2, float hue)
{
   if (hue < 0.0)
      hue += 1.0;
   else if (hue > 1.0)
      hue -= 1.0;
   float res;
   if ((6.0 * hue) < 1.0)
      res = f1 + (f2 - f1) * 6.0 * hue;
   else if ((2.0 * hue) < 1.0)
      res = f2;
   else if ((3.0 * hue) < 2.0)
      res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
   else
      res = f1;
   return res;
}

vec3 HSLToRGB(vec3 hsl)
{
   vec3 rgb;
   
   if (hsl.y == 0.0)
      rgb = vec3(hsl.z); // Luminance
   else
   {
      float f2;
      
      if (hsl.z < 0.5)
         f2 = hsl.z * (1.0 + hsl.y);
      else
         f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
         
      float f1 = 2.0 * hsl.z - f2;
      
      rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));
      rgb.g = HueToRGB(f1, f2, hsl.x);
      rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));
   }
   
   return rgb;
}


/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
*/

// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
   // Increase or decrease theese values to adjust r, g and b color channels seperately
   const float AvgLumR = 0.5;
   const float AvgLumG = 0.5;
   const float AvgLumB = 0.5;
   
   const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
   
   vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
   vec3 brtColor = color * brt;
   vec3 intensity = vec3(dot(brtColor, LumCoeff));
   vec3 satColor = mix(intensity, brtColor, sat);
   vec3 conColor = mix(AvgLumin, satColor, con);
   return conColor;
}


/*
** Float blending modes
** Adapted from here: http://www.nathanm.com/photoshop-blending-math/
** But I modified the HardMix (wrong condition), Overlay, SoftLight, ColorDodge, ColorBurn, VividLight, PinLight (inverted layers) ones to have correct results
*/

#define BlendLinearDodgef          BlendAddf
#define BlendLinearBurnf          BlendSubstractf
#define BlendAddf(base, blend)       min(base + blend, 1.0)
#define BlendSubstractf(base, blend)    max(base + blend - 1.0, 0.0)
#define BlendLightenf(base, blend)       max(blend, base)
#define BlendDarkenf(base, blend)       min(blend, base)
#define BlendLinearLightf(base, blend)    (blend < 0.5 ? BlendLinearBurnf(base, (2.0 * blend)) : BlendLinearDodgef(base, (2.0 * (blend - 0.5))))
#define BlendScreenf(base, blend)       (1.0 - ((1.0 - base) * (1.0 - blend)))
#define BlendOverlayf(base, blend)    (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))
#define BlendSoftLightf(base, blend)    ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))
#define BlendColorDodgef(base, blend)    ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))
#define BlendColorBurnf(base, blend)    ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))
#define BlendVividLightf(base, blend)    ((blend < 0.5) ? BlendColorBurnf(base, (2.0 * blend)) : BlendColorDodgef(base, (2.0 * (blend - 0.5))))
#define BlendPinLightf(base, blend)    ((blend < 0.5) ? BlendDarkenf(base, (2.0 * blend)) : BlendLightenf(base, (2.0 *(blend - 0.5))))
#define BlendHardMixf(base, blend)    ((BlendVividLightf(base, blend) < 0.5) ? 0.0 : 1.0)
#define BlendReflectf(base, blend)       ((blend == 1.0) ? blend : min(base * base / (1.0 - blend), 1.0))


/*
** Vector3 blending modes
*/

// Component wise blending
#define Blend(base, blend, funcf)       vec3(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b))

#define BlendNormal(base, blend)       (blend)
#define BlendLighten            BlendLightenf
#define BlendDarken            BlendDarkenf
#define BlendMultiply(base, blend)       (base * blend)
#define BlendAverage(base, blend)       ((base + blend) / 2.0)
#define BlendAdd(base, blend)       min(base + blend, vec3(1.0))
#define BlendSubstract(base, blend)    max(base + blend - vec3(1.0), vec3(0.0))
#define BlendDifference(base, blend)    abs(base - blend)
#define BlendNegation(base, blend)    (vec3(1.0) - abs(vec3(1.0) - base - blend))
#define BlendExclusion(base, blend)    (base + blend - 2.0 * base * blend)
#define BlendScreen(base, blend)       Blend(base, blend, BlendScreenf)
#define BlendOverlay(base, blend)       Blend(base, blend, BlendOverlayf)
#define BlendSoftLight(base, blend)    Blend(base, blend, BlendSoftLightf)
#define BlendHardLight(base, blend)    BlendOverlay(blend, base)
#define BlendColorDodge(base, blend)    Blend(base, blend, BlendColorDodgef)
#define BlendColorBurn(base, blend)    Blend(base, blend, BlendColorBurnf)
#define BlendLinearDodge         BlendAdd
#define BlendLinearBurn         BlendSubstract
// Linear Light is another contrast-increasing mode
// If the blend color is darker than midgray, Linear Light darkens the image by decreasing the brightness. If the blend color is lighter than midgray, the result is a brighter image due to increased brightness.
#define BlendLinearLight(base, blend)    Blend(base, blend, BlendLinearLightf)
#define BlendVividLight(base, blend)    Blend(base, blend, BlendVividLightf)
#define BlendPinLight(base, blend)       Blend(base, blend, BlendPinLightf)
#define BlendHardMix(base, blend)       Blend(base, blend, BlendHardMixf)
#define BlendReflect(base, blend)       Blend(base, blend, BlendReflectf)
#define BlendGlow(base, blend)       BlendReflect(blend, base)
#define BlendPhoenix(base, blend)       (min(base, blend) - max(base, blend) + vec3(1.0))
#define BlendOpacity(base, blend, F, O)    (F(base, blend) * O + blend * (1.0 - O))


// Hue Blend mode creates the result color by combining the luminance and saturation of the base color with the hue of the blend color.
vec3 BlendHue(vec3 base, vec3 blend)
{
   vec3 baseHSL = RGBToHSL(base);
   return HSLToRGB(vec3(RGBToHSL(blend).r, baseHSL.g, baseHSL.b));
}

// Saturation Blend mode creates the result color by combining the luminance and hue of the base color with the saturation of the blend color.
vec3 BlendSaturation(vec3 base, vec3 blend)
{
   vec3 baseHSL = RGBToHSL(base);
   return HSLToRGB(vec3(baseHSL.r, RGBToHSL(blend).g, baseHSL.b));
}

// Color Mode keeps the brightness of the base color and applies both the hue and saturation of the blend color.
vec3 BlendColor(vec3 base, vec3 blend)
{
   vec3 blendHSL = RGBToHSL(blend);
   return HSLToRGB(vec3(blendHSL.r, blendHSL.g, RGBToHSL(base).b));
}

// Luminosity Blend mode creates the result color by combining the hue and saturation of the base color with the luminance of the blend color.
vec3 BlendLuminosity(vec3 base, vec3 blend)
{
   vec3 baseHSL = RGBToHSL(base);
   return HSLToRGB(vec3(baseHSL.r, baseHSL.g, RGBToHSL(blend).b));
}


/*
** Gamma correction
** Details: http://blog.mouaif.org/2009/01/22/photoshop-gamma-correction-shader/
*/

#define GammaCorrection(color, gamma)                        pow(color, 1.0 / gamma)

/*
** Levels control (input (+gamma), output)
** Details: http://blog.mouaif.org/2009/01/28/levels-control-shader/
*/

#define LevelsControlInputRange(color, minInput, maxInput)            min(max(color - vec3(minInput), vec3(0.0)) / (vec3(maxInput) - vec3(minInput)), vec3(1.0))
#define LevelsControlInput(color, minInput, gamma, maxInput)            GammaCorrection(LevelsControlInputRange(color, minInput, maxInput), gamma)
#define LevelsControlOutputRange(color, minOutput, maxOutput)          mix(vec3(minOutput), vec3(maxOutput), color)
#define LevelsControl(color, minInput, gamma, maxInput, minOutput, maxOutput)    LevelsControlOutputRange(LevelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput)

// added by Eric so it works in Magic:
void main() {
   
    vec4 col1 = texture2D(iChannel0, gl_FragCoord.xy / iResolution.xy);
    vec4 col2 = texture2D(iChannel1, gl_FragCoord.xy / iResolution.xy);

    // replace BlendPhoenix with any other mode from above:
    gl_FragColor = vec4( BlendPhoenix(col1.rgb, col2.rgb), (col1.a+col2.a)/2.0);
}

To try it out, just download the attached BlendModes.txt file below, and load it into the GLSLShader module.

BlendModes.txt
(10.18 KiB) Downloaded 453 times

By default, the file uses the "BlendPhoenix" mode (an arbitrary decision by me). The slightly tricky part is that if you want to change the mode, you have to edit my code, but it's a VERY minor change. If you scroll to the very bottom you can see the line I added which has the text "BlendPhoenix" in it. BlendPhoenix is only one of the many modes available. To change the mode, replace BlendPhoenix with one of the other options higher in the file, such as BlendLinearDodge or BlendGlow.
Code: Select all
gl_FragColor = vec4( BlendGlow(col1.rgb, col2.rgb), (col1.a+col2.a)/2.0);

In other words, all you have to do is copy and paste! Hopefully that's not too hard.

Just note that, to use more than one mode at a time in your Magic project, you'll have to create a new text file for each. Again though, it's just copying and pasting.

Here's an example screenshot:
Image

I'm sure some of you will have questions so feel free to ask! :)

Enjoy,
Eric
Eric
Site Admin
 
Posts: 2637
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Thu Sep 18, 2014 6:32 am

Wonderful. Thanks Eric! I'm making myself a set of these shaders with the appropriate blendmode names.

Sorry, I'm a complete noob and am struggling to understand GLSL.
Could one use a conditional statement to control the blendmode by your GLSLShader module's X param?

This would greatly facilitate experimentation, even if there's a performance hit.
http://www.gamedev.net/topic/543939-glsl-conditional-statement-causes-performance-hit/

Maybe there doesn't need to be a performance hit:
https://www.packtpub.com/books/content/glsl-40-using-subroutines-select-shader-functionality

Once again I'm blown away by the power of Magic.

Terry
Terry Payman
 
Posts: 542
Joined: Sun Sep 14, 2014 8:15 am
Location: UK

Re: Photoshop blending modes for the GLSLShader module

Thu Sep 18, 2014 3:36 pm

Good questions Terry. It doesn't seem like you are a noob. :)

Conditional statements usually do cause a performance hit, so that's why I didn't put them in. The other method (using subroutines) is a good alternative, but it's not supported by the GLSLShader module right now unfortunately.

You're right though, even with the performance hit, it would be nice to be able to experiment by using the X param. Let me see if I can put something together.

Eric
Eric
Site Admin
 
Posts: 2637
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Fri Sep 19, 2014 10:05 am

Thanks Eric.
Please also see my feature request for choosing one of these blend modes for playlist scene transitions.
http://magicmusicvisuals.com/forums/viewtopic.php?f=4&t=156&p=430#p430
Terry Payman
 
Posts: 542
Joined: Sun Sep 14, 2014 8:15 am
Location: UK

Re: Photoshop blending modes for the GLSLShader module

Tue Sep 30, 2014 4:05 pm

Here's a version of the shader which lets you select one of 28 blend modes by using the X Param.

The mode is selected in increments of .01, starting at 0. So, for example, if you want blend mode 14, set the value of the X Param to 0.14. You can Ctrl-click (or Cmd-click on OSX) on the X Param's black arrow to increment the value by .01 so you can easily scroll through all the modes one by one.

You'll have to look at the file to see which number corresponds with which mode. Can't think of any other way to do it at the moment :).

Hope it helps.

Eric
Attachments
BlendModes_Multi.txt
(12.82 KiB) Downloaded 376 times
Eric
Site Admin
 
Posts: 2637
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Thu Oct 02, 2014 2:24 am

Awesome! :D
Many thanks Eric!

An invaluable aid for optimising blend modes and experimentation with a variety of sources.
For even more variety it seemed useful to swap the inputs over too, although this affected only some of the modes.

I made a quad split screen to further facilitate testing all the blend combinations, with and without input swaps, with a variety of inputs.
This was my Magic scene:
2zgtb0j.jpg
2zgtb0j.jpg (113.16 KiB) Viewed 7487 times


This produced the image below (showing my two favourite blends from the many options, not the difference when inputs are swapped).
Two source feeds display in the top-left and bottom-left quadrants. Blended outputs displayed top-right and bottom-right.
3aqt2.jpg
3aqt2.jpg (161.4 KiB) Viewed 7487 times


Speed tests on a low-power rig:
Frame rate with two BlendModes_Multi shaders 57fps (Xparam=0.15 for ColorBurn )
Frame rate with two BlendColorBurn shaders 60fps (with no conditionals)

...so as you predicted there is a hit from the conditionals if one uses the _Multi shader, but during design it's a small price to pay for the amazing speed of searching the many blend combinations to pick an optimum. Thanks again! :D

Terry
Terry Payman
 
Posts: 542
Joined: Sun Sep 14, 2014 8:15 am
Location: UK

Re: Photoshop blending modes for the GLSLShader module

Thu Oct 02, 2014 4:19 pm

Thanks Terry, I'm sure that will be very helpful for everyone.

The order of the inputs does indeed affect some of the modes, but not all. It's because of the order of mathematical operations. For example, 3+2 is the same as 2+3, but 3-2 isn't the same as 2-3.

I think it's the same in Photoshop -- the blend mode will be affected by which layer is on top. Am I remembering that correctly?

Eric
Eric
Site Admin
 
Posts: 2637
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Thu Oct 02, 2014 11:54 pm

Yes, Photoshop blend depends on layer order sometimes.
Terry Payman
 
Posts: 542
Joined: Sun Sep 14, 2014 8:15 am
Location: UK

Return to Tutorials, etc.

© 2019 Color & Music, LLC • This web site is mobile-friendly