semi-transparency and <body> background color

Hi,

I load a PNG texture with an 8 bits alpha channel. Transparency pixels are ok: I can see what was in the framebuffer before I draw.

But semi-transparent ones (0.0 < alpha < 1.0) are blended with the <body> background color! In fact, it seems like there were 3 colors in the calculation: the framebuffer one (destination), the source and the <body> background color. My expectation is that the <body> bgcolor should not interfere (I clear my whole framebuffer to a different color with gl.clear(r, g, b, 1.0)).

I did a test to change the <body> background color each frame and I confirm the behaviour. Mainly, I use that usual OpenGL setup:

gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

The behaviour is the same on Firefox and Chrome. What could be wrong?

Rendering order is important. It sounds blending is enabled when you put down the first layer after clearing the background. This might cause the 1st layer to blend with the background. Then your second layer blends with the blending in the framebuffer. It is possible that you can correct this problem by not enabling blending until you are ready to render the second layer.

Better than words: test

That is curious. I was so focused on webgl that I missed that the html <body> settings were creeping into the webgl blending. This looks like a flaw that is deep in the browser implementation. There are probably many browser details that could use refining. In the general webgl forum I have a post about how attempts to devote all of the client space to a canvas result in browser slider bars. You are right about the code being more clear than words, you only have one layer of rendering so my suggestions about disabling blending was never of any use.

The problem is the same with Firefox and Chrome. I suspect a GPU driver bug as the blending pipeline is too critical to be done out of the GPU without dramatically slowing things anyway. I wonder how it could be possible that a third parameter is taken into account into the blending equation (GL_CONSTANT color maybe?).

I test on NVidia and I don’t have another GPU to test it right now, what’s yours?

I was curious about that as well. I have a ATI/AMD 4850 so it is probably the browser and not in the drivers. I am sure these browsers are using GPU acceleration to speed up non-webgl rendering. The background blend would be appropriate if you were displaying an RGBA <img>. The weirdest aspect is how A=0 uses the webgl background while larger values blend with the HTML background.

Nice point, but not exactly: that would imply only 2 arguments to the equation either way (which, at least, would not be that strange).

But in fact, the webgl background (the framebuffer destination) is still taken into account. I modified the test so the clear color is a deep blue to amphasis this. You can see that the blending implies the 3 colors: the <body> bgcolor, the source (texture) and the destination (deep blue).

Wow, just woke up and a light bulb was suddenly blinding me! I could not wait to turn on my computer and try:

gl.colorMask(true, true, true, false);

Dammit! It did the job :stuck_out_tongue: Now I think about it, it makes sense!

Be careful though: my first reflex was to do it just as a GL initialization, but all I got was a plain white canvas (whatever happens next). Here’s what to do… just after GL initialization:

	gl.colorMask(true, true, true, true);
	gl.clearColor(0.0, 0.0, 0.0, 1.0);
	gl.clear(gl.COLOR_BUFFER_BIT);
	gl.colorMask(true, true, true, false);

Just do this one time and all the rest will be ok without modifications (the first line is just to make things clear as after GL init, this is the default anyway).

Of course, if you play with glColorMask(), don’t forget to restore normal behavior with the last line because if you say true as alpha argument, the behavior will appear again. You could also only just put the last line just after your clear call in your main loop.

This is not a “fire & forget” workout though. This completely disables alpha writing and there may be some circumstances where you NEED alpha values to be written in your operations, so you have to keep that in mind.

Ok, my last trick was doing well with Firefox, but sucked with Chrome. It’s a shame, I though I could sort it out just with initializations, but no. Just forget it.

So, the only real thing to consider is that the WebGL’s default FBO is drawn with blending enabled by browsers (this is usually not the case on desktop OpenGL where the default FBO alpha values are generally unused for that purpose since it is simply swapped, and even if windowed).

To avoid the leakage with the <body> background behind the canvas, we must then only disable alpha writes when blending stuffs in it. This is not that heavy since anyway, blending stuff should be batched for performances reasons and be done after all other drawing operations.

So just before blending batch:

gl.colorMask(true, true, true, false);

And restore normal operations once finished:

gl.colorMask(true, true, true, true);

This is not only cleaner to restore, but makes the behaviour correct on Chrome (I had bad results otherwise, sounds like Chrome applies different blending parameters than FF).

I updated my test, with a check button to see the difference with and without the counter-measure. It works the same in FF4, FF5 and Chrome.