I am drawing flat colors and textures into WebGL canvas. My colors and textures have varying alpha values and I want them to be blended correctly. I want to have transparent background (they should be blended with HTML content, which is under canvas).
In WebGL, I use
gl.clearColor(0, 0, 0, 0);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.BLEND);
It works correctly, when HTML background is black. But when I set a JPG pattern as a background of , I draw black triangle (alpha=1) and white triangle (alpha=0.5), I can see the background pattern in place where triangles intersect each other. Is this correct behavior?
My earlier answer was actually incorrect. That is the expected result of what you describe.
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
means that the resulting alpha isIn your example, after the black triangle (with alpha=1) is drawn, a drawn pixel in the framebuffer will have an alpha of
So it will be fully opaque. Next, after the white triangle (with alpha=.5) is drawn, a pixel in the intersection of the two triangles will have an alpha of
That means the final color will be treated as partially transparent, and, when it is composited with the page, the page background will show through.
This is a somewhat uncommon problem in regular OpenGL, since content is usually composited against an empty background. It does come up when you draw to an FBO and have to composite the results with other content in your context, though. There are a few ways to deal with this.
One way is to have your alpha blend with
gl.ONE
,gl.ONE_MINUS_SRC_ALPHA
so you getwhich is what you usually want with alpha blending. However, you want your colors to still blend with
gl.SRC_ALPHA
,gl.ONE_MINUS_SRC_ALPHA
. You can use gl.blendFuncSeparate instead ofgl.blendFunc
to set your color blending and alpha blending functions separately. In this case, you would callAnother option is to take advantage of colors with premultiplied alpha (which WebGL actually already assumes you are using, for instance, when sampling a color from a texture). If you draw the second triangle with the alpha already multiplied through the color (so a half transparent white triangle would have
gl_FragColor
set tovec4(.5, .5, .5, .5)
), then you can use the blend modeand it will act as you want for both color and alpha.
The second option is what you'll commonly see in WebGL examples, since (as mentioned), WebGL already assumes your colors are premultiplied (so
vec4(1., 1., 1., .5)
is out of gamut, the rendering output is undefined, and the driver/GPU can output any crazy color it wants). It's far more common to seegl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
in regular OpenGL examples, which leads to some confusion.