【问题标题】:OpenGL ReadPixels (Screenshot) AlphaOpenGL ReadPixels(截图)Alpha
【发布时间】:2014-07-20 09:12:56
【问题描述】:

我正在使用平铺渲染(使用 glReadPixels)设置来截取场景的屏幕截图。我的输出看起来正确:

但是查看 Alpha 通道,即使透明纹理渲染在不透明纹理之上,像素仍然是透明的。

特别是汽车的“内部”在我们看到天花板的地方应该是完全不透明的,但在穿过后窗时应该是部分透明的。

有没有一种方法可以在一个像素处测试每个纹理的 alpha 分量,而不仅仅是最接近的那个?

【问题讨论】:

  • 您可以对 alpha 使用添加混合功能,而不是您当前使用的任何功能。这将完全解决您的问题(因为汽车天花板上的 alpha 将被限制为 1.0)。您甚至可以对颜色和 alpha 使用单独的混合功能。这当然假设您有一个实际存储目标 alpha 的颜色缓冲区,这不是传统渲染所必需的,但由于您正在读取 alpha 通道,所以我必须假设您已经以这种方式配置了帧缓冲区。
  • 有趣的想法,但我不认为它完全适合我。如果玻璃是半透明的,那么它将是 50% + 50% = 100% 不透明。

标签: opengl rendering screenshot alpha


【解决方案1】:

有没有一种方法可以在一个像素处测试每个纹理的 alpha 分量,而不仅仅是最接近的那个?

没有。 OpenGL为每个像素存储一个RGBA值;无法获取之前的值(因为这需要大量 RAM)。

写入帧缓冲区的 alpha 值取决于您的 alpha 混合方程,您可以使用 glBlendFuncglBlendFuncSeparate 进行设置。有关详细信息,请参阅 OpenGL wiki 上的 blending pagethis JavaScript app 可让您查看各种混合模式的效果。

【讨论】:

  • 有一种方法,但它有点小技巧。有一种称为模板路由 a 缓冲的技术,它基本上使用多采样纹理中的样本存储多层 alpha 透明几何图形。它通常用于诸如与顺序无关的透明度以及将 alpha 混合集成到延迟着色引擎中,而无需为半透明几何体编写前向着色后备。然而,正如您所说,存储需求大幅增加。
【解决方案2】:

我认为您从 cmets 和其他答案中获得了一些有用的方向,但没有详细说明解决方案。与其只给出结果,不如让我来介绍一下,以便您下次知道如何自己解决。

我假设您使用GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 混合函数将半透明对象从后向前绘制。你没有直接提到这一点,但它是相当标准的,并且与你所看到的一致。正确的解决方案还需要帧缓冲区中的 alpha 组件,但您已经拥有了,否则在读取 alpha 时您将一无所获。

为了说明整个过程,我将在途中使用两个示例。我将只列出颜色的 (R, A) 组件,GB 的行为就像 R

  1. 绘制一个颜色为(R1, 1.0) 的图层,然后在其上绘制一个带有(R2, 0.4) 的图层。
  2. 绘制一个颜色为(R1, 0.5) 的图层,然后在其上绘制一个带有(R2, 0.4) 的图层。

背景颜色是(Rb, 0.0),对于这种混合,您总是希望使用 0.0 的 alpha 值来清除。

首先,让我们计算一下我们想要为颜色实现的结果:

  • 对于案例 1,绘制第一层完全覆盖了背景,因为它的 alpha = 1.0。然后我们在上面混合第二层。由于它的 alpha = 0.4,我们保留第一层的 60%,并添加第二层的 40%。所以我们想要的颜色是

    0.6 * R1 + 0.4 * R2

  • 对于情况 1,绘制第一层会保留 50% 的背景背景,因为它的 alpha = 0.5。所以到目前为止的颜色是

    0.5 * Rb + 0.5 * R1

    然后我们在上面混合第二层。我们再次保留之前颜色的 60%,并添加第二层的 40%。所以我们想要的颜色是

    0.6 * (0.5 * Rb + 0.5 * R1) + 0.4 * R2 = 0.3 * Rb + 0.3 * R1 + 0.4 * R2

现在,让我们弄清楚我们希望 alpha 的结果是什么:

  • 对于案例 1,我们的第一层是完全不透明的。查看不透明度的一种方法是衡量物体吸收了多少光。一旦我们有了一个吸收所有光线的层,我们渲染的任何其他东西都不会改变它。我们的总 alpha 应该是

    1.0

  • 对于情况 2,我们有一层吸收 50% 的光,还有一层吸收剩余光的 40%。由于 50% 的 40% 是 20%,所以总共吸收了 70%。我们的总 alpha 应该是

    0.7

使用GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA 进行混合可为您提供所需的颜色结果。但正如您所注意到的,不适用于 alpha。对示例进行计算:

  • 案例1:绘制图层1,SRC_ALPHA为1.0,源值为S = (R1, 1.0),目标值为D = (Rb, 0.0)。所以混合函数的计算结果为

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 1.0 * (R1, 1.0) + 0.0 * (Rb, 0.0) = (R1, 1.0)

    这被写入帧缓冲区,并成为绘制第 2 层的目标值。第 2 层的源是(R2, 0.4)。用 0.4 评估 SRC_ALPHA 给出

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.4 * (R2, 0.4) + 0.6 * (R1, 1.0) = (0.4 * R2 + 0.6 * R1, 0.76)

  • 案例2:绘制图层1,SRC_ALPHA为0.5,源值为S = (R1, 0.5),目标值为D = (Rb, 0.0)。所以混合函数的计算结果为

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.5 * (R1, 0.5) + 0.5 * (Rb, 0.0) = (0.5 * R1 + 0.5 * Rb, 0.25).

    这被写入帧缓冲区,并成为绘制第 2 层的目标值。第 2 层的源是(R2, 0.4)。用 0.4 评估 SRC_ALPHA 给出

    SRC_ALPHA * S + ONE_MINUS_SRC_ALPHA * D = 0.4 * (R2, 0.4) + 0.6 * (0.5 * R1 + 0.5 * Rb, 0.25) = (0.4 * R2 + 0.3 * R1 + 0.3 * Rb, 0.31).

所以我们确认了您已经知道的:我们得到了所需的颜色,但 alpha 值错误。我们如何解决这个问题?我们需要一个不同的 alpha 混合函数。幸运的是,OpenGL 有glBlendFuncSeparate(),它允许我们做到这一点。我们只需要弄清楚 alpha 使用什么混合函数。以下是思考过程:

假设我们已经渲染了一些半透明对象,总 alpha 为A1,存储在帧缓冲区中。到目前为止,我们渲染的内容吸收了总光的一部分 A1,并让一部分 1.0 - A1 通过。我们在上面渲染另一个带有 alpha A2 的层。该层吸收了之前通过的光的一部分A2,因此它吸收了所有光的额外(1.0 - A1) * A2。我们需要将此添加到已经吸收的光量中,这样现在总共吸收了(1.0 - A1) * A2 + A1

剩下要做的就是将其转换为 OpenGL 混合方程。 A2 是源值SA1 是目标值D。所以我们想要的 alpha 结果变成了

(1.0 - A1) * A2 + A1 = (1.0 - A1) * S + 1.0 * D

我所说的A1是framebuffer中的alpha值,在混合函数规范中称为DST_ALPHA。所以我们使用ONE_MINUS_DST_ALPHA 来匹配我们的源乘数1.0 - A1。我们使用GL_ONE 来匹配目标乘数1.0

所以alpha的blend函数参数是(GL_ONE_MINUS_DST_ALPHA, GL_ONE),完整的blend函数调用是:

glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
                    GL_ONE_MINUS_DST_ALPHA, GL_ONE);

我们可以再用示例再次检查 alpha 的数学:

  • 案例1:绘制图层1,DST_ALPHA为0.0,源值为S = (.., 1.0),目标值为D = (.., 0.0)。所以混合函数的计算结果为

    ONE_MINUS_DST_ALPHA * S + ONE * D = 1.0 * (.., 1.0) + 1.0 * (.., 0.0) = (.., 1.0)

    这被写入帧缓冲区,并成为绘制第 2 层的目标值。第 2 层的源是 (.., 0.4)DST_ALPHA 现在是 1.0。评估第 2 层的混合方程给出了

    ONE_MINUS_DST_ALPHA * S + ONE * D = 0.0 * (.., 0.4) + 1.0 * (.., 1.0) = (.., 1.0)

    我们得到了所需的 alpha 值1.0

  • 案例2:绘制图层1,DST_ALPHA为0.0,源值为S = (.., 0.5),目标值为D = (.., 0.0)。所以混合函数的计算结果为

    ONE_MINUS_DST_ALPHA * S + ONE * D = 1.0 * (.., 0.5) + 1.0 * (.., 0.0) = (.., 0.5)

    这被写入帧缓冲区,并成为绘制第 2 层的目标值。第 2 层的源是(.., 0.4)DST_ALPHA 现在是 0.5。评估第 2 层的混合方程给出了

    ONE_MINUS_DST_ALPHA * S + ONE * D = 0.5 * (.., 0.4) + 1.0 * (.., 0.5) = (.., 0.7)

    我们得到了所需的 alpha 值 0.7

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 2011-10-03
    • 2015-07-25
    • 1970-01-01
    • 2011-12-14
    • 2015-08-08
    相关资源
    最近更新 更多