How to do REAL subtractive blending

kburkhart84

Firehammer Games
I've looked in the manual about it...the subtractive blending offered there isn't really subtractive. All the blend modes there multiply the source and destination by something, and then add those together. What if I want it to multiply them by something, and then subtract them. This is doable in OpenGL by a separate function call, but it appears that it is not available to us. Am I missing something? Thanks in advance.
 

Binsk

Member
gpu_set_blendmode(bm_subtract) == gpu_set_blendmode_ext(bm_zero, bm_inv_src_color);
bm_zero = (R * 0, G * 0, B * 0, A * 0)
bm_inv_src_color = (1 - sR, 1 - sG, 1 - sB, 1 - sA);

If your dest color is white (1, 1, 1, 1) and you want to subtract blue (0, 0, 1, 1) you should get yellow with no alpha (1, 1, 0, 0).
color1 = (0 * 0, 0 * 0, 0 * 1, 0 * 1) = (0, 0, 0, 0)
color2 = (1 * (1 - 0), 1 * (1 - 0), 1 * (1 - 1), 1 * (1 - 1)) = (1, 1, 0, 0)

Add them together gives you (0, 0, 0, 0) + (1, 1, 0, 0) = (1, 1, 0, 0), aka., yellow with no alpha.
It subtracts as you expect, what's the problem?
 

kburkhart84

Firehammer Games
source = 0.5, 0.5, 0.5, 0.5
dest = 0.25, 0.25, 0.25, 0.25



bm_zero = 0, 0, 0, 0 regardless of the colors
bm_inv_src_color = (0.5, 0.5, 0.5, 0.5)

add those together, you get 0.5 all around. 0.5 - 0.25 is not 0.5. Maybe my logic is wrong or I'm missing something.

EDIT********************************

I was missing something, but it still isn't adding up.

Source 0.25, 0.25, 0.25, 0.25
Dest 0.75, 0.75, 0.75, 0.75
Should result in 0.5, 0.5, 0.5, 0.5

bm_zero = 0, 0, 0, 0
bm_inv_src_color = 0.75, 0.75, 0.75, 0.75

color1 = 0*0.25, 0*0.25, 0*0.25, 0*0.25 = 0, 0, 0, 0
color2 = 0.75 * 0.75, 0.75 * 0.75, 0.75 * 0.75, 0.75 * 0.75 = 0.5625, 0.5625, 0.5625, 0.5625
color1 + color2 = the same as color2 since color1 is 0.
0.5626 != 0.5.

Source 0.33, 0.33, 0.33, 1
Destination 0.75, 0.75, 0.75, 1
Should give 0.42, 0.42, 0.42, 0

bm_zero = 0, 0, 0, 0
bm_inv_src_color = 0.67, 0.67, 0.67, 0

color1 = 0*0.33, 0*0.33, 0*0.33, 0*1 = 0, 0, 0, 0
color2 = 0.75 * 0.67, 0.75 * 0.67, 0.75 * 0.67, 1 * 0 = 0.5025, 0.5025, 0.5025, 0
color1 + color2 = color2(color1 = 0 anyway), but 0.5025 != 0.42 that should be the result.
 
Last edited:

Binsk

Member
Hum... You are correct. In that case, I am unsure as to how you would do that with pure blend modes. I know that even the blend-modes we are given aren't supported on all platforms. I would guess the reasoning as to no "subtract" operation would be similar to their reasoning for that.

I suppose you could use a shader instead. When you draw your image you could pass in the destination as a sampler and do the exact math that you want there. When you do this you could use the blend mode of (bm_one, bm_zero) and that would apply it properly.

Albeit this would be a tad more complicated since you'd have to pass in coord data relative to your render target and you'd likely have to have an intermediary surface (since rendering to the same target that you pulled the sample from can be an issue).
 

kburkhart84

Firehammer Games
Yeah, it just feels strange that they would call it bm_subtract, but then it isn't right. The values are pretty close, and it seems that at the least the result is always "within range" if you get what I mean. A true subract blend mode would work with both source and destination being bm_one, and then those subtracting instead of adding. There is a function in the OpenGL API that changes that operation, but Yoyo has not ported that specific call to GML as of yet(that I know of anyway).
 

CMAllen

Member
No, it is subtractive, exactly as it says. You're just looking at it from color blending perspective and not the math perspective. Black is 0 across all color channels. White is 255 across all color channels. Any value minus 0 is unchanged, so a black pixels subtracts nothing, leaving the original pixel unchanged. Any color value minus 255 is zeroed out, so a white pixel subtracts all value from all color channels, leaving only black. Same principle, just reversed from how most people think of subtractive color blending.

What I want are my three favorite color blending methods -- soft light, hard light, and overlay, though I could get away with any one of the three.
 
A

Ampersand

Guest
No, it is subtractive, exactly as it says. You're just looking at it from color blending perspective and not the math perspective. Black is 0 across all color channels. White is 255 across all color channels. Any value minus 0 is unchanged, so a black pixels subtracts nothing, leaving the original pixel unchanged. Any color value minus 255 is zeroed out, so a white pixel subtracts all value from all color channels, leaving only black. Same principle, just reversed from how most people think of subtractive color blending.

What I want are my three favorite color blending methods -- soft light, hard light, and overlay, though I could get away with any one of the three.
This is correct. Even according to your math @kburkhart84 this is still subtractive blending just like in Lightroom or Photoshop. I do agree it is a bit of a misnomer if you're thinking of it in a visual light sense, but it's one of those things that's just kind of stuck.
 

CMAllen

Member
This is correct. Even according to your math @kburkhart84 this is still subtractive blending just like in Lightroom or Photoshop. I do agree it is a bit of a misnomer if you're thinking of it in a visual light sense, but it's one of those things that's just kind of stuck.
Yeah, it's definitely reversed from how most image editors handle subtractive blending, so it confuses most people, but once you understand the math involved, it gets a little clearer to visualize.
 

kburkhart84

Firehammer Games
I don't disagree that it is subtractive(why I used the term "within range" above). It just isn't EXACT subtractive, at least on a 0 - 1 scale. 0.75 - 0.25 ends up being 0.5625 instead of 0.5. But yeah, the result will always be a value below what the destination was originally. It's almost like they were on separate scales, like it was converted to HSV first or something similar, because at the top of the scale, like full white, yellow, etc... it works exact, full white minus full blue DOES work out to full yellow with this method, but when you get to values less than 1 and more than 0, it stops being exact.
 
Top