OpenGL ReadPixels(截图)Alpha

时间:2014-05-30 14:52:27

标签: opengl rendering screenshot alpha

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

Full image

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

Alpha Mask

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

是否有一种方法可以测试像素上每个纹理的alpha分量,而不仅仅是最近的一个?

2 个答案:

答案 0 :(得分:2)

  

是否有一种方法可以测试像素上每个纹理的alpha分量,而不仅仅是最近的一个?

没有。 OpenGL为每个像素存储一个RGBA值;没有办法获得以前的值(因为那需要大量的RAM)。

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

答案 1 :(得分:2)

我认为你从评论和其他答案中得到了一些有用的指导,但没有详细解决方案。让我走过去,而不是仅仅给出结果,这样你就知道下次自己如何解决这个问题了。

我假设你使用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)的图层。
  3. 背景颜色为(Rb, 0.0),您总是希望清除这种混合的alpha值为0.0。

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

    • 对于案例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进行混合可为您提供所需的颜色效果。但正如你所注意到的,不是为了阿尔法。对示例进行计算:

    • 案例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)。使用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)。使用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)

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

    假设我们已经渲染了一些半透明物体,其总alpha值为A1,它存储在帧缓冲区中。到目前为止我们渲染的内容会吸收总光线的一小部分A1,然后让一部分1.0 - A1通过。我们在其上面渲染另一个带有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的是帧缓冲区中的alpha值,在混合函数规范中称为DST_ALPHA。因此,我们使用ONE_MINUS_DST_ALPHA来匹配1.0 - A1的源倍增器。我们使用GL_ONE来匹配目标乘数1.0

    因此alpha的混合函数参数为(GL_ONE_MINUS_DST_ALPHA, GL_ONE),完整的混合函数调用为:

    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