• Godot HelpShaders
  • Lerp towards white for pixels with alpha to fix blending modes shader? (Image inside)

Hi. New to Godot, new to shaders. Managed to steal together an image blending shader alá Photoshop to use in a TextureRect, simplified for this example. But it's not respecting the alpha of the png like Photoshop does. During an Add blend, for example, areas with transparency should turn towards black to fix the issue. I want to do this (and white and gray) within the shader. Can someone advice?

And here's the shader. Put in in a TextureRect with an image.

shader_type canvas_item;
render_mode blend_mix; // Is this required?

// MODES
//0 - Multiply
//1 - ADD
//2 - HARDLIGHT
//3 - HARDLIGHT 2nd method
uniform int mode :hint_range(0,3) = 1;
uniform float opacity :hint_range(0, 1) = 0.5;

vec4 multiply(vec4 base, vec4 blend){
	return base * blend;
}

vec4 add(vec4 base, vec4 new){
	return base+new;
}

vec4 hard_light(vec4 base, vec4 new){
	vec4 limit = step(0.5, new);
	return mix(2.0 * base * new, 1.0 - 2.0 * (1.0 - base) * (1.0 - new), limit);
}

void fragment(){
	vec4 base = texture(SCREEN_TEXTURE, SCREEN_UV);
	vec4 new = texture(TEXTURE,UV);
	vec4 b = base.argb;
	vec4 n = new.argb;
	vec4 output=vec4(0,0,0,0);
	
	switch(mode){
		case 0: // MULTIPLY - new's alpha must be white
			output=multiply(b,n);
			break;
		case 1: // ADD - new's alpha must be black
			output=add(b,n);
			break;
		case 2: // HARDLIGHT - new's alpha must be mid-gray
			output = hard_light(b,n);
			break;
		case 3: // HARDLIGHT 2nd method
			vec4 c_u = n * b * 2.0;
			vec4 c_d = 1.0 - (1.0 - n) * (1.0 - b) * 2.0;
			output = mix(c_u, c_d, b + 0.5);
			break;
	}
	//COLOR=output;
	//Originally it was all vec3, but since I'm trying to involve alpha I switched to vec4
	COLOR=vec4(vec3(output[1],output[2],output[3]),opacity);
}

Also, I suspect that last line is stupid, somehow. Any constructive criticism is welcome.

Not sure about the algorithm, but you are never taking alpha into account so it won't work.

@cybereality said: Not sure about the algorithm, but you are never taking alpha into account so it won't work.

I did it!

You got me thinking on how to isolate the alpha. Ended up with float a = fg[3] (fg: foreground texture vec3 to be blended). Then I could make a mask and screen or multiply it or its negative. New shader can multiply, add, hardlight and color burn while respecting the alpha channel of the foreground image.

BTW, vec4 b = bg.rgba; and vec4 b = bg.argb both output the same vector with alpha at index 3. EDIT- GLSL indicates it doesn't do anything.vec4 b = bg.rgba; is the same as vec4 b = bg;. I removed it from the code below.

Here's the working version.

shader_type canvas_item;
render_mode blend_mix; // Is this required?

// MODES
//0 - Multiply
//1 - ADD
//2 - HARDLIGHT
//3 - COLORBURN
uniform int mode :hint_range(0,3) = 1;
uniform float opacity :hint_range(0, 1) = 0.5;

vec4 screen(vec4 bg, vec4 fg){
	return 1.0-(1.0-bg)*(1.0-fg);
}
vec4 multiply(vec4 bg, vec4 fg){
	return bg * fg;
}
vec4 add(vec4 bg, vec4 fg){
	return bg+fg;
}
vec4 colorBurn(vec4 bg, vec4 fg){
	return 1.0 - (1.0 - bg) / fg;
}
vec4 hard_light(vec4 bg, vec4 fg){
	vec4 limit = step(0.5, fg);
	return mix(2.0 * bg * fg, 1.0 - 2.0 * (1.0 - bg) * (1.0 - fg), limit);
}

void fragment(){
	vec4 bg = texture(SCREEN_TEXTURE, SCREEN_UV);
	vec4 fg = texture(TEXTURE,UV);
	vec4 output=vec4(0,0,0,0);
	
	float a=fg[3]; // alpha value
	
	switch(mode){
		case 0: // MULTIPLY
			fg=screen(fg,vec4(1.0-a,1.0-a,1.0-a,1)); // whiten the transparent
			output=multiply(bg,fg);
			break;
		case 1: // ADD
			fg=multiply(fg,vec4(a,a,a,1)); // blacken the transparent
			output=add(bg,fg);
			break;
		case 2: // HARDLIGHT
			fg=multiply(fg,vec4(a,a,a,1)); // blacken the transparent
			fg=screen(fg,vec4(0.5-a,0.5-a,0.5-a,1)); //whiten the transparent till mid-gray
			output = hard_light(bg,fg);
			break;
		case 3: // COLORBURN
			fg=screen(fg,vec4(1.0-a,1.0-a,1.0-a,1)); // whiten the transparent
			output=colorBurn(bg,fg);
			break;
	}
	COLOR=vec4(vec3(output[0],output[1],output[2]),opacity);
}

Learning Godot isn't as horrible as I had feared.

a year later