Talk:Blending

From OpenGL Wiki
Jump to: navigation, search

Blending of Alpha

The text below is from http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/ - once we get this page fixed, I'll remove it from my blog post.

I noticed that the OpenGL wiki's blending page I link to has an error. It says to use these settings if your source (and destination) is premultiplied:

glBlendEquationSeparate(GL_FUNC_ADD,GL_FUNC_ADD);

glBlendFuncSeparate(GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ZERO); // not correct for the general case

This is not general - it assumes the destination's alpha is zero (which means the destination's fully transparent), as it simply sets the final alpha to be the same as the source alpha.

The proper settings are:

glBlendEquationSeparate(GL_FUNC_ADD,GL_FUNC_ADD);

glBlendFuncSeparate(GL_ONE,GL_ONE_MINUS_SRC_ALPHA,GL_ONE,GL_ONE_MINUS_SRC_ALPHA);

This computes the final alpha as the source alpha's area plus the remaining area times the destination alpha's coverage, classic Porter & Duff. Might as well get it right since it costs nothing extra to compute. I tried to change the entry on the wiki, but it was reverted - discussion has commenced.


All the above said, I could be wrong - the OpenGL API for blending is one area that's tricky to understand, and I wouldn't at all be shocked if I'm misinterpreting here. GL_ONE,GL_ZERO to me means "multiply the source alpha by one, the destination alpha by zero", which would then mean you're saving just the source alpha, clearly incorrect if there's a destination alpha > 0.

The question is this: what does the destination alpha actually mean? The meaning of a value is defined by how it gets used. And in most cases, the destination alpha in blending operations is *never used*. You can say that it means opaque or transparent, but if you're never using the framebuffer image's destination alpha (whether in a later blend operation or a fragment shader processing step or whatever), then it doesn't mean a thing. Most of the time, its value is meaningless and therefore it may as well be the source alpha of the last pixel rendered.
Now, there may be specific cases (multi-compositing) where the destination alpha has a purpose. But in those cases, there is a second rendering operation that reads from the image that was written to. Which means the alpha has an explicit purpose, determined by the person viewing it. But without that context, no alpha can be said to be "incorrect" relative than another. That's why I added a section explaining about the blending of the alpha for the non-premultiplied case. Alfonse (talk) 11:48, 26 January 2016 (EST)
I'm happy to see you left in my addition of "glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);". Better yet, in my opinion, is that this line should simply replace the one above it, "glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);" With the GL_ONE, GL_ZERO setting the alpha returned is always equal to the source alpha. The GL_ONE, GL_ONE_MINUS_SRC_ALPHA always gives the right answer, and as far as I know costs nothing extra (the compositor is hardwired on GPUs). If people don't care about the alpha, these new GL_ONE, GL_ONE_MINUS_SRC_ALPHA settings work. If people don't bother setting the destination alpha and simply leave it to be 1.0, these new settings work. If people do have an alpha set for the destination alpha, these new settings then give the proper blended alpha. The GL_ONE, GL_ZERO work for the first two cases, "don't care" and "return source alpha", but don't work for the last case.
Let's put it another way. If "GL_ONE, GL_ONE_MINUS_SRC_ALPHA" was the original text on this page, what justification would there be to change it to "GL_ONE, GL_ZERO"? The only case I can see is where you want to return just the source alpha, in which case the right way to do this is to initialize the destination alpha to all zeros (along with initializing the destination color to all zeros, which is what you would do if you want just the source color for your cutout). I think it's worth teaching people the clear and consistent way to composite.
@Alfonse: you are absolutely right that your choice of blending function depends on your intent, what you want to do with the alpha. (And I know @EricHaines doesn't dispute that.) But please consider his proposed change, for these additional reasons:
- It makes sense for "over operator" blending to be the default. The over operator is the most common intent for blending when people use glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). You're right that very often with OpenGL, people discard the destination alpha, but some people just don't realize that a correct over operator with straight alpha blending is to use glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA). If the over operator is the intent, as it very commonly is, then the best suggestion for separate blending functions is the one Eric suggested with alpha blending of GL_ONE, GL_ONE_MINUS_SRC_ALPHA.
- a straight alpha source and glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); is the equivalent of using a premultiplied source and glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- It is becoming more common over time to use destination alpha and composite multiple layers using over-operator blending. WebGL, for example, *always* uses destination alpha to composite your buffer over the HTML page, unless you explicitly turn it off.
- If it's important to note that destination alpha isn't used, then why use glBlendFuncSeparate() at all? Why complicate it, why not use glBlendFunc()? Using glBlendFuncSeparate() with specific parameters makes the code look like there was a specific intent for destination alpha. If there is no specific intent, maybe it's better to avoid code that appears to declare intent?
- If destination alpha isn't used, there is still a good semantic reason to make it look like an over operator, when the over operator actually is the blending intent - which it often is. Compositing straight alpha sources over a background is better written as glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) than any other way, because it computes the correct "over operator" destination alpha, even if you choose not to use it. And when someone later decides to use the destination alpha, the result will be correct and no code debugging or refactoring will be needed.
- Maybe it would be better to have examples or to describe what effects the different blending functions given can be used for? Maybe including pictures of the resulting color & alpha buffers would help the most? The over operator blending is explained a bit, but your alpha blending func GL_ONE,GL_ZERO isn't justified explicitly, and it could be. There are good reasons to choose that blending function, one of them is filling the alpha channel in between layers. The alpha blending func GL_ZERO,GL_ONE is a great way to achieve lens flare and other additive effects, where the proper intent is to discard source alpha. The alpha blend func GL_ONE,GL_ONE_MINUS_SRC_ALPHA is the best way to achieve over operator layering. --Dahart (talk) 18:54, 18 June 2016 (EDT)