我正在使用此问题的答案中描述的方法将笔触绘制到帧缓冲区对象:
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时产生的(错误的)图像:
这是将图层直接绘制到屏幕上时的正确结果:
谢谢你的帮助。
最佳答案
我不确定“摆脱预乘的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
因此,
newPixelRed
和newPixelAlpha
完全与绘制之前的区域相同。使
newPixelRed
返回其未预乘状态的唯一方法是除以alphanewPixelRed = Red(0.14) / Alpha(0.2)
newPixelRed = 0.14 / 0.2
newPixelRed = 0.7 <- The original red before it was premultiplied
您只能在着色器或JavaScript中执行此操作。单独混合不会做到这一点。