Blending with HTML background in WebGL

前端 未结 1 1496
無奈伤痛
無奈伤痛 2021-01-13 05:25

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

相关标签:
1条回答
  • 2021-01-13 05:45

    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) means that the resulting alpha is

    A_final = A_s * A_s + (1 - A_s) * A_d
    

    In your example, after the black triangle (with alpha=1) is drawn, a drawn pixel in the framebuffer will have an alpha of

    1 * 1 + (1 - 1) * 0 == 1 + 0 == 1
    

    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

    .5 * .5 + (1 - .5) * 1 == .25 + .5 == .75
    

    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 get

    A_final = A_s + (1 - A_s) * A_d
    

    which 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 of gl.blendFunc to set your color blending and alpha blending functions separately. In this case, you would call

    gl.BlendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    

    Another 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 to vec4(.5, .5, .5, .5)), then you can use the blend mode

    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)
    

    and 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 see gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) in regular OpenGL examples, which leads to some confusion.

    0 讨论(0)
提交回复
热议问题