保存使用 glReadPixels 读取的图像,其中 alpha 值不是一

Posted

技术标签:

【中文标题】保存使用 glReadPixels 读取的图像,其中 alpha 值不是一【英文标题】:Saving images read with glReadPixels where alpha value is not one 【发布时间】:2015-03-05 16:34:21 【问题描述】:

当使用glReadPixels 保存图像时,颜色会在其 alpha 值小于 1 的位置出现失真。

表面由 QtQuick 管理。使用glGetInteger,我发现每个通道都有 8 位,包括 alpha。

使用这样的方法,我可以获得更好的结果,但并不完美:

for x := 0; x < m.Bounds().Dx(); x++ 
    for y := 0; y < m.Bounds().Dy(); y++ 
        c := m.RGBAAt(x, y)
        w := float64(c.A) / 255
        c.R = uint8(float64(c.R)*w + 255*(1-w) + 0.5)
        c.G = uint8(float64(c.G)*w + 255*(1-w) + 0.5)
        c.B = uint8(float64(c.B)*w + 255*(1-w) + 0.5)
        c.A = 255
    m.SetRGBA(x, y, c)
    

我尝试使用以下方法清除 OpenGL 本身中的 alpha 组件:

s.gl.ClearColor(0, 0, 0, 1)
s.gl.ColorMask(false, false, false, true)
s.gl.Clear(GL.COLOR_BUFFER_BIT)

现在的结果与我的手动构图相似,而且显示和捕获的图像相同,但仍然与之前显示的不同(并且比之前显示的更暗)。

我对 OpenGL/Qt 在显示颜色缓冲区时如何使用 alpha 通道感兴趣。也许 QtQuick 用一个支持层来组合它?

【问题讨论】:

您熟悉预乘 alpha 吗?您的解决方案基于它(尽管有一些我无法解释的多余细节)。但是,如果您不确定它为什么会起作用,我会觉得您可能不会。 不,我没有。感谢您为我指明正确的方向。如果我理解正确,我必须将图像与白色背景混合。但为什么?无论如何,我尝试了一些东西,考虑到 glReadPixels 的输出是预乘的,但我仍然无法得到精确的结果。另外,为什么这是预乘?你能帮我找到相关的文件吗?谢谢。 比如标签的颜色(RGBA 133,30,30,159)怎么会变成白色(RGB 255,255,255)? 最常用的预乘 alpha 用于 alpha 合成。预乘 alpha 混合是关联的,它允许更多的灵活性。它还避免了过滤透明像素的问题;在非透明像素之间进行插值时,具有 0 alpha 的像素应忽略其 RGB 颜色。提前将 RGB 乘以 A 可以在数学上实现这一点,而不是使用像 if (alpha == 0.0) ... 这样愚蠢的东西。至于颜色,如果使用预乘 alpha 存储帧缓冲区,那么您应该能够简单地将 RGB 除以 A 以获得正确的颜色。 不是这样:在上面的例子中,133,30,30,159 应该变成 255,255,255。一定有别的东西。 【参考方案1】:

我通过在绘图期间从不更改 alpha 解决了这个问题。因此,我现在使用gl.BlendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) 代替gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA),并修改了其他参数以使其看起来像以前一样。

正如 Andon M. Coleman 在他的 cmets 中指出的那样,这与使用预乘 alpha 混合相同。这样,颜色缓冲区的 alpha 值始终保持为 1,并且问题得到解决。

glBlendFuncSeparate,分别为 RGB 和 alpha 分量指定像素算术,对于获得相同的结果很有用。

【讨论】:

以上是关于保存使用 glReadPixels 读取的图像,其中 alpha 值不是一的主要内容,如果未能解决你的问题,请参考以下文章

使用 OpenCV 处理来自 glReadPixels 的图像并将其作为纹理返回

如何保存帧缓冲区然后将其取回

需要一些手来渲染、显示和保存图像

如何使用 glReadPixels 读取浮点数据

在 Android 中使用 Open GL Framebuffer 时如何转换图像

使用 glReadPixels 读取 rgb 值时,OpenGLES Alpha 值会修改它们