12

I was using glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) for alpha composing as the document said (and actually same thing was said in the Direct3D document).

Everything was fine at first, until I downloaded the result from GPU and made it a PNG image. The result alpha component is wrong. Before drawing, I had cleared the frame buffer with opaque black colour. And after I drew something semi-transparent, the frame buffer became semi-transparent.

BlueWanderer
  • 2,671
  • 2
  • 21
  • 36

1 Answers1

17

Well the reason is obvious. With glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), we actually ignore the destination alpha channel and assume it always be 1. This is OK when we treat the frame buffer as something opaque.

But what if we need the correct alpha value? glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA) and make the source premultiplied (premultiplied texture/vertex color or multiply the alpha component with alpha components before setting it to gl_FragColor).

glBlendFunc can only multiply the original color components with one factor, but alpha compositing needs the destination be multiplied with both one_minus_src_alpha and dst_alpha. So it must be premultiplied. We can't do the premultiplication in the frame buffer, but as long as the source and destination are both premultipied, the result is premultipied. That is, we first clear the frame buffer with any premultiplied color (for example: 0.5, 0.5, 0.5, 0.5 for 50% transparent white instead of 1.0, 1.0, 1.0, 0.5), and draw premultipied fragments on it with glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA), and we will have correct alpha channel in the result. But remember to undo the premultiplication if it is not desired for the final result

BlueWanderer
  • 2,671
  • 2
  • 21
  • 36
  • Another important thing to consider: does your framebuffer even have destination alpha bit-planes? This is not a common pixel format, believe it or not. Usually you get something like RGBx8 (x means ignored), Depth:Stencil (24:8) unless you explicitly ask for RGBA. You would think this would be obvious, but since destination alpha is not needed for general alpha blending, people often overlook this... – Andon M. Coleman Oct 17 '13 at 00:19
  • You can use glBlendFuncSeparate to specify separate blend functions for rgb and alpha, which will allow you to get correct alpha values for compositing without the requirement to use premultiplied alpha. – rdb Jun 28 '16 at 02:06
  • @rdb, I'm interested in an example with the glBlendFuncSeparate you mention. Could you provide a typical values for this method? – mgouin Mar 13 '19 at 02:38
  • @mgouin For example: `glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)` – rdb Mar 13 '19 at 10:42
  • @rdb: this is incorrect. To correctly blend into an RGBA framebuffer you need to divide the destination RGB by destination A after the blending, but none of the OpenGL blending modes allow that. – Yakov Galka Apr 27 '19 at 21:34
  • @YakovGalka Could you elaborate on that? I'm interested. Could you mention the steps to produce a correct RGBA blend? Thanks! – mgouin Jan 05 '21 at 20:06
  • @mgouin: If your texture and framebuffer are both premultiplied (or have no alpha channel) then use `glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)`. If your framebuffer is premultiplied (or has no alpha) but your texture is straight alpha, then use `glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)` as @rdb said. However, you cannot have a framebuffer with straight alpha using the built-in GPU blending operations -- which is what this question was about. – Yakov Galka Jan 05 '21 at 20:30