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

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。即当我使用

glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);

将图层绘制到另一个FBO中,然后使用glReadPixel()读取该FBO的内容,则该内容仍会预乘。

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

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

javascript - 用预乘Alpha读出FBO-LMLPHP

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

javascript - 用预乘Alpha读出FBO-LMLPHP

谢谢你的帮助。

最佳答案

我不确定“摆脱预乘的alpha”是什么意思。如果您希望图片未预乘,则必须在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中执行此操作。单独混合不会做到这一点。

09-30 15:50