与中间帧缓冲区混合的正确擦除方法

Correct way to erase for paint blending with intermediate framebuffers

我的场景由渲染到帧缓冲区的图层组成。在帧缓冲区中,元素从后向前绘制,并与 ColorWithAlphaEraseWithAlpha 混合。帧缓冲区与相应的 *WithPremultipliedAlpha 版本混合。以下代码控制混合:

switch (m_blendMode)
{
case BlendMode::ColorWithAlpha:
{
    glBlendEquation(GL_FUNC_ADD);
    glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    break;
}
case BlendMode::ColorWithPremultipliedSrcAlpha:
{
    glBlendEquation(GL_FUNC_ADD);
    glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    break;
}
case BlendMode::EraseWithAlpha:
{
    glBlendEquation(GL_FUNC_ADD);
    glBlendFuncSeparate(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
    break;
}
case BlendMode::EraseWithPremultipliedSrcAlpha:
{
    glBlendEquation(GL_FUNC_ADD);
    glBlendFuncSeparate(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
    break;
}

我正在努力跨帧缓冲区边界进行擦除。使用 ColorWithAlpha 将所有元素从后向前绘制到一个帧缓冲区中相当于将除一个元素之外的所有元素绘制到一个帧缓冲区中,然后使用 ColorWithAlpha 将最后一层绘制到单独的帧缓冲区中,然后混合单独的帧缓冲区使用 ColorWithPremultipliedAlpha 转换为原始文件。擦除并非如此。我尝试使用 ColorWithAlphaEraseWithPremultipliedAlpha 从后往前绘制图层,以将目标 FBO 与目标 FBO 混合。不幸的是,这不等同于将顶部 FBO 中的所有元素直接绘制到具有 EraseWithAlpha 的目标 FBO 中。是否可以使擦除混合模式具有关联性,启用与着色模式相同的设置?如果没有,是否有关联的伪擦除混合模式可以在这里交换?

数学正确。结果证明这是由 alpha 通道中的量化伪像引起的。当使用 ColorWithAlpha 将要擦除的图层从后拉到中间帧缓冲区中时,浮点截断会减少累积的 alpha。生成的 alpha 是

destAlpha * (1 - (a[i] + ((1 - a[i])a[i-1]) ...)

其中 i 从 0 到层数 - 1。当使用 EraseWithAlpha 从后向前绘制时,结果 alpha 是

destAlpha[i] * (1 - a[i])

这两个重复之间的唯一区别是关联。对于每个擦除层按顺序绘制到目标中的情况,在初始绘制之后,目标 alpha 乘以所有层的组合 alpha。对于帧缓冲区的情况,目标 alpha 仅在末尾考虑,导致不同的浮点截断。如果有一种聪明的方法可以使这两个用例的浮点错误以相同的方式累积,请发表评论。现在我已经解决了这个问题,方法是确保需要重绘的层按照与原始绘制相同的方式分组。