OpenGL blending - output alpha value unused

Hello,

I have a question about blending.

Rendering on a black background and trying to add a second surface to a first surface using:

glBlendFuncSeparate		(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);		// default
glBlendEquationSeparate	(GL_FUNC_ADD, GL_FUNC_ADD);				// default

Depending on the order in which the surfaces are drawn, I get the following results:

											rgba		rgb			src_rgb	dst_rgb	src_a	dst_a

src		0.00	0.00	1.00	0.50		0000FF80	000080FF	1.00	0.00	1.00	0.00
dst		0.50	0.00	0.00	1.00		800000FF	800000FF
out		0.00	0.00	1.00	0.50		0000FF80	000080FF	out_rgba rgb part used; out_rgba a part unused; out_rgb unused;

and

											rgba		rgb			src_rgb	dst_rgb	src_a	dst_a

src		1.00	0.00	0.00	0.50		FF000080	800000FF	1.00	0.00	1.00	0.00
dst		0.00	0.00	0.50	1.00		000080FF	000080FF						
out		1.00	0.00	0.00	0.50		FF000080	800000FF	out_rgba rgb part used; out_rgba a part unused; out_rgb unused;

By sampling the output colors from the screen, it appears that the RGB portion of o_rgba (i.e. #0000FF and #FF0000) is being used as the output color, where o_rgb was expected, and that the A portion of o_rgba (i.e. #80 and #80) is not being used to compute o_rgb or at least o_rgb is not the color on the screen.

I’ve tried to solve this by inspecting glColorMask(), but all values are set to GL_TRUE.

Using

glBlendFunc		(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

I get comparable results. Again it is the RGB portion of o_rgba that ends up on the screen.

Can someone please explain?

Best regards,
Mischa Baars, the Netherlands.

I don’t understand what the numbers in the table represent. In particular, what is o_rgba as distinct from o_rgb? I would think that they’re the output RGBA and RGB values, but that would mean that the first three values of o_rgba should be identical to o_rgb. But they’re not.

So what are you doing? How are you even figuring out what o_rgba/o_rgb are? The framebuffer does not contain 7 color channels; it only has 4.

Your blending-related question(s) are not obvious. And even more obscure is what you are fundamentally trying to accomplish with blending.

Could you list your blending-related questions out, briefly, in a bulleted list?

  • Question 1
  • Question 2
  • …

And as Alfonse said, the data you provide is also confusing. I think I infer the meaning of most of it, but I wouldn’t bet on it.

Above, you set up alpha blending to:

  1. use the SOURCE RGB and ALPHA at 100%,
  2. use the DEST RGB and ALPHA at 0%,
  3. and ADD these together, component-wise.

…yielding literally the SOURCE RGB and SOURCE ALPHA. (See: glBlendFuncSeparate(), glBlendEquationSeparate())

But then you seem surprised when you get exactly this behavior. You seem to be expecting that something else should happen (…where ALPHA values of source or dest are being used to compute RGB values). And you think this is a problem that needs solved.

If you want some other blending behavior, you need to setup your blending differently. As far as I can tell, you’re getting exactly the behavior you asked for.

What behavior is it you are trying to achieve and why?


You probably want to spend some time playing with this:

and reading these:

to see graphically how this works and to learn how to configure OpenGL blending to meet your needs.

Apparently I was not clean enough. I changed the row names of the table to src, dst and out, and the columns o_rgba and o_rgb to rgba and rgb.

The first three columns are the src, dst and out colors in vec4 format, the fourth is these colors in hex, the fifth is these colors as they appear on the screen (e.g. vec4(src.rgb * src.a, +1.00f) in hexadecimal). I’ve given you two out values, because the answer is dependent on the order of drawing the objects (in most cases). The values are computed using Libreoffice Calc.

It is also dependent on the format being used to feed colors into the blender and on the way you treat colors in the fragment shader. I should have given you eight out values for completeness.

I do not exactly get the behaviour I expected.

I expected to see (FF, 00, 00, 80) = (80, 00, 00(, FF)), where the output is indeed equal to the input. Instead, I get to see (FF, 00, 00(, FF))?! Like the a part of out_rgba is completely being ignored.

Configuring glColorMask was the first solution I thought of, but all values are set to GL_TRUE by default. So that can’t be it.

The issue probably arises within GTK and inside the GtkGLArea and has probably to do with OpenGL internal formats being used to configure renderbuffer and textures (https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetInternalformat.xhtml). I’ve been playing with it late this afternoon, and indeed the output is affected by changing the internal format, but it’s too early to get into detail.

Any other ideas are welcome.

What behavior is it you are trying to achieve and why?

I’m trying to configure the blender such that the order in which surfaces are rendered does not influence the outcome. This would mean that an object being rendered is invariant under rotation, in other words a red semi-transparent cube with blue semi-transparent front rotated over the y-axis in the positive direction is drawn with the same colors as this cube rotated over this y-axis in the negative direction. That’s a good thing.

I noticed that most (all Linux) browsers (and GTK as well) do not care about this behaviour. Adding CSS colors is non-commutative.

Ok. In other words, you want a blending function that is commutative. That is, B blend on A yields the same result as A blend on B.

Most blending functions/configs are not commutative. There are exceptions though. For instance, you could setup a MAX blending function with ONE factors for SOURCE and DEST, clear screen to black (0,0,0), and then render your objects. Or you could ADD with ONE factors on both. See glBlendFuncSeparate() for more ideas. It’s just a matter of what artistic look you’re going for.

That’s because it is.

The destination alpha means exactly and only what your blending equation says that it means. There is no inherent functionality or meaning to alpha. Alpha means “transparent” only to the extent that you explicitly do math that makes it mean “transparent”.

A destination alpha of 0.5 doesn’t do anything by itself. It has no effect on the other destination colors, and when OpenGL tries to display the image, nothing special happens because of that destination alpha.

Hi Alfonse,

Only this is about the blender output, and not about the blender source and destination. What happens to the blender output alpha?

I do see I made a mistake in calling glBlendFuncSeparate() en glBlendEquationSeparate() with their default arguments, because I’m using semi-transparent red and semi-transparent blue as source and destination, thereby skipping the first step, namely adding semi-transparent red or semi-transparent blue to black.

Let’s do that:

glBlendFuncSeparate		(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO);		// default
glBlendEquationSeparate	(GL_FUNC_ADD, GL_FUNC_ADD);				// default
										rgba		src_rgb	dst_rgb	src_a	dst_a
													
src	0.00	0.00	1.00	0.50		0000FF80	1.00	0.00	1.00	0.00
dst	0.00	0.00	0.00	1.00		000000FF	
out	0.00	0.00	1.00	0.50		0000FF80	
													

The color that appears on the screen is (00,00,FF). The blender’s output alpha is completely ignored. If, like you say, that’s normal, then why give formulas how to compute it?

Think I’m starting to get the hang of this:

The blender output color is drawn on the screen with the alpha omitted. The blender output color is propagated to the next blender surface with the alpha still in place.

So when we add blue to black we see (00,00,FF) on the screen and (00,00,FF,80) as the next blender destination color.

What does your blend equation say happens to it? As previously mentioned, alpha doesn’t do anything unless you make it do something.

It is not “ignored”; it is what your math says it is. Your math defines how to compute the output colors. Your math does not say to compute the RGB component by doing something with either the source or destination alpha component. Therefore, the source and destination alpha have no effects on the RGB of the output.

Blending functionality takes a source color (4-element vector) and a destination color (4-element vector), does some math on the based on the parameters you provide, and generates an output color (4-element vector). If you want to create transparency, then you need to have the blending system do specific math that models something being see-through.

And setting the parameters as you have is not that particular math. Your settings indicate that the output color shall be the source color. It’s an exact copy of source to output for all 4 components.

It would be no different from not using blending at all.

It is used when writing the input of the blender’s next function evaluation. It is unused when writing the output to the screen, same as when blending is disabled. I agree.

I’ve just written a small C program that tests most input combinations of glBlendFuncSeparate en glBlendEquationSeparate for commutativity, 6^6*5^2 in total for now. There are still a few details to implement. So …

Tomorrow is another day.

Great!

It would be more correct to say that, at the end of all rasterization, the framebuffer pixel’s color (RGBA) is drawn on the screen with the alpha omitted (so RGB only).

Prior to this, this color value (RGBA) is updated based on how you’ve configured blending, color write masking, and other fragment-related operations that affect drawing to that pixel.

Yes, if you setup the pipeline to write the blender output color (including alpha) to the framebuffer color buffer.

It’s probably better here to say that the framebuffer color’s alpha value “is available for use” as an input to the next rasterization pass using blending (GL_BLEND enabled).

This subsequent rasterization pass can choose to use this alpha value (termed the “destination alpha” value) or not. That’s determined by the blend function setup.

In glBlendFuncSeparate(), search for DST_ALPHA for blending functions that pull in this “destination alpha” value, which is already in the framebuffer color prior to this rendering pass.

Unless your OS/window system compositor can be configured to make use of the alpha channel for blending the window contents with the rest of the desktop - of course that requires system specific settings and is out of scope for OpenGL itself :wink: See e.g. GLFW window transparency.

1 Like

Hi Carsten,

That’s one other point of interest. GTK does not make use of GLFW, but I do need to explicitly set the widget background to black if my GL_COLOR_CLEAR_VALUE is set to black. If I set the widget background to red, and leave the GL_COLOR_CLEAR_VALUE set to black, you see that the blue front quad shifts into the red.

I was getting into this.

In a glance, it looks like my program is rendering into the first color attachment GL_COLOR_ATTACHMENT0 buffer and GTK is rendering into the GL_BACK_LEFT buffer. I am as you sort of a newbie (to OpenGL), but my guess is it has something to do with the accumulation buffer.

It would be more correct to say

Thanks your for helping!

It’s probably better here to say

Thanks again!

Do you know what happens at the boundaries? According to the reference page of glBlendFunc, the upper (+1.00f) boundary is protected by a MIN(), but I see nothing about the lower (+0.00f) boundary.

Without the boundaries protected, I get this when adding red and blue with an alpha of 0.25.

combination exceeds bounds: (GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA), (GL_FUNC_SUBTRACT, GL_FUNC_SUBTRACT)

black + red        : (+0.00f, +0.00f, +0.00f, -1.00f) = (00, 00, 00, 01)
black + red  + blue: (+0.00f, +0.00f, +2.00f, -0.50f) = (00, 00, FE, 80)

black + blue       : (+0.00f, +0.00f, +0.00f, -1.00f) = (00, 00, 00, 01)
black + blue + red : (+2.00f, +0.00f, +0.00f, -0.50f) = (FE, 00, 00, 80)

black + red        : (+0.00f, +0.00f, +0.00f, -1.00f) = (00, 00, 00, 01)
black + red  + red : (+0.00f, +0.00f, +0.00f, -1.00f) = (00, 00, 00, 01)

The method presented in https://learnopengl.com/Advanced-OpenGL/Blending would have required doing rotations twice, both on the cpu and the gpu, but now …

Here’s the complete list of input combinations to glBlendFuncSeparate and glBlendEquationSeparate that result in commutative blending. All of these blender configurations are invariant under rotation.

No need to sort objects anymore!

Enjoy!


Oops, can only upload image and model file formats. Sorry! This will have to do.