使用预乘 Alpha 读出 FBO

Reading out an FBO with Premultiplied Alpha

我正在使用这个问题的答案中描述的方法向帧缓冲区对象绘制笔触:

opengl - blending with previous contents of framebuffer

此方法正确的 alpha - 将不同的 OpenGL 绘图操作混合到一个 FBO 中并确保 alpha 保持正确。

它使用

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

将 OpenGL 绘图操作(在我的例子中是笔触)混合到 FBO 中并使用

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

将最终的 FBO(在我的例子中是类似 Photoshop 的层)绘制回屏幕,这会删除该方法中使用的预乘 alpha。将 FBO 传送到屏幕时,这对我来说效果很好。

但是,当我想读出FBO的内容时,我无法摆脱预乘的alpha。 IE。当我使用

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

将图层绘制到另一个FBO中,而不是使用glReadPixel()读出这个FBO的内容,内容仍然是预乘的。

换句话说,当我将 FBO 绘制到屏幕上时,混合并移除 prem。 alpha 工作,当做同样的事情并绘制到 FBO 时,它失败了。

这是我读出 FBO 时产生的(错误的)图像:

下面是将图层直接绘制到屏幕时的正确结果:

感谢您的帮助。

我不确定 "get rid of the premultiplied alpha" 是什么意思。如果您希望图片为 un-premultiplied,那么您必须在 JavaScript 或带有

之类的着色器中手动取消预乘
gl_FragCoord = vec4(color.a > 0. ? color.rgb / color.a : vec3(0), color.a);

当您绘制到 canvas 时它起作用的原因是因为默认情况下 canvas 需要预乘 alpha。使用 ONE 渲染,ONE_MINUS_SRC_ALPHA 是使用预乘 alpha 绘制并保持结果预乘

示例:

Un-premultiplied    Red = 0.7  Alpha = 0.2

    convert to premultiplied   Red = Red * Alpha

Premultiplied       Red = 0.14 Alpha = 0.2

    blend with ONE,ONE_MINUS_SRC_ALPHA

                    DstRed = 0.0  DstAlpha = 0.0

    newPixelRed    = Red(0.14) * ONE + DstRed(0.0) * (1 - Alpha(0.2))
    newPixelRed    = 0.14 +            0.0 * 0.8
    newPixelRed    = 0.14

    newPixelAlpha  = Alpha(0.2) * ONE + DstAlpha(0.0) * (1 - Alpha(0.2))
    newPixelAlpha  = 0.2 + 0.0 * 0.8
    newPixelAlpha  = 0.2

因此 newPixelRednewPixelAlpha 区域与您绘制之前的区域完全相同。

newPixelRed 回到其未预乘状态的唯一方法是除以 alpha

newPixelRed = Red(0.14) / Alpha(0.2)
newPixelRed = 0.14 / 0.2
newPixelRed = 0.7            <- The original red before it was premultiplied

您只能在着色器或 JavaScript 中执行此操作。单独混合是不行的。