Q : Is there a way to use the default pipeline to blend the Alpha component properly?
Problem : I'm drawing semi-transparent surfaces into a texture, then I want to blit that texture into the main frame back buffer. Normally when you use straightforward alpha blending for transparency or antialiasing, it is to blend color components into the back buffer ( which has no alpha channel by deafult ).
However, when blending into a texture that has an alpha component, the usual GL_SRC_ALPHA, GL_ONE_MINUS_DST_ALHPA does not blend the "alpha" component properly :
Notice the problematic zone over the death star at the edge of the red and blue circles; the alpha component is supposed to be completely opaque. Note that the RGB components are blendend as expected.
So I played around with different settings for the BlendFunc and the closest to what I want is by using GL_DST_ALPHA as my dst alpha scaling factor.
There is still a problem with this approach because blending two surfaces with 50% opacity should give a combined surface of 75% opacity. But using OpenGL default formulas, I get (0.50.5) + (0.50.5) = 0.5. More disturbing, if I have 2 surfaces with 40% opacity, I get a LESS opaque surface : (.4*.4)+(.4*.4) = 0.32.
The real formula ( 1 - ( 1 - srcAlpha ) * ( 1 - dstAlpha ) ) would imply that I could use a GL_FUNC_MULT instead of GL_FUNC_ADD in the blend equation for the alpha component. Unfortunately this does not exist.
So I gave up and tried to do a solution using a shader. This becomes complicated really fast if the texture you are rendering to is also the texture you read from. Especially if you render multiple semi-transparent surfaces.
I'm looking for alternative solutions I have not already tried. Any idea is welcome.
EDIT : I also tried GL_ONE as both src and dst scaling factor. this makes the alpha component way too opaque as 1*.5 + 1*.5 will give 1 as the resulting alpha. Makes me wonder if GL_ONE and GL_DST_ALPHA would give better results.
p.s. : I used Anders Riggelsen's blending tool for the images.
The standard technique to do perfect blending is to use
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Note that it requires both the source and destination to be premultiplied, and produces premultiplied result.
Premultiplication basically means performing color.rgb *= color.a
on the color. Usually you apply it to the textures when you load them, or to the final color in your shader.
Corollary: if alpha == 1
, premultiplication makes no difference.
If the source is not premultiplied, but the destination is, use this instead:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
Yes, kind of.
Blending factors ONE_MINUS_DST_ALPHA, ONE may be used for blending alpha correctly. If you have glBlendFuncSeperate you can set different blend factors for color and alpha. Operation is add.
I say kind of because as I recall seperate blend func appeared late in the deprecated API.
Edit: after doing the math those blend factors are mathematically equivalent to HolyBlackCat answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With