与中间帧缓冲区混合的正确擦除方法
Correct way to erase for paint blending with intermediate framebuffers
我的场景由渲染到帧缓冲区的图层组成。在帧缓冲区中,元素从后向前绘制,并与 ColorWithAlpha
或 EraseWithAlpha
混合。帧缓冲区与相应的 *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
转换为原始文件。擦除并非如此。我尝试使用 ColorWithAlpha
和 EraseWithPremultipliedAlpha
从后往前绘制图层,以将目标 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 仅在末尾考虑,导致不同的浮点截断。如果有一种聪明的方法可以使这两个用例的浮点错误以相同的方式累积,请发表评论。现在我已经解决了这个问题,方法是确保需要重绘的层按照与原始绘制相同的方式分组。
我的场景由渲染到帧缓冲区的图层组成。在帧缓冲区中,元素从后向前绘制,并与 ColorWithAlpha
或 EraseWithAlpha
混合。帧缓冲区与相应的 *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
转换为原始文件。擦除并非如此。我尝试使用 ColorWithAlpha
和 EraseWithPremultipliedAlpha
从后往前绘制图层,以将目标 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 仅在末尾考虑,导致不同的浮点截断。如果有一种聪明的方法可以使这两个用例的浮点错误以相同的方式累积,请发表评论。现在我已经解决了这个问题,方法是确保需要重绘的层按照与原始绘制相同的方式分组。