Previous topicNext topic

Photoshop blending modes for the GLSLShader module

Tutorials, FAQs, resources, and examples for using Magic.
Post Reply
Magic
Site Admin
Posts: 3440
Joined: Wed Apr 09, 2014 9:28 pm

Photoshop blending modes for the GLSLShader module

Post by Magic »

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/ ... l-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/vie ... ?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 1822 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
Terry Payman
Posts: 710
Joined: Sun Sep 14, 2014 8:15 am
Location: UK
Contact:

Re: Photoshop blending modes for the GLSLShader module

Post by Terry Payman »

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-gls ... mance-hit/

Maybe there doesn't need to be a performance hit:
https://www.packtpub.com/books/content/ ... ctionality

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

Terry
Magic
Site Admin
Posts: 3440
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Post by Magic »

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
Terry Payman
Posts: 710
Joined: Sun Sep 14, 2014 8:15 am
Location: UK
Contact:

Re: Photoshop blending modes for the GLSLShader module

Post by Terry Payman »

Thanks Eric.
Please also see my feature request for choosing one of these blend modes for playlist scene transitions.
http://magicmusicvisuals.com/forums/vie ... p=430#p430
Magic
Site Admin
Posts: 3440
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Post by Magic »

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 1665 times
Terry Payman
Posts: 710
Joined: Sun Sep 14, 2014 8:15 am
Location: UK
Contact:

Re: Photoshop blending modes for the GLSLShader module

Post by Terry Payman »

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 34668 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 34668 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
Magic
Site Admin
Posts: 3440
Joined: Wed Apr 09, 2014 9:28 pm

Re: Photoshop blending modes for the GLSLShader module

Post by Magic »

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
Terry Payman
Posts: 710
Joined: Sun Sep 14, 2014 8:15 am
Location: UK
Contact:

Re: Photoshop blending modes for the GLSLShader module

Post by Terry Payman »

Yes, Photoshop blend depends on layer order sometimes.
Post Reply