alpha混合公式的主要问题在于源/目标/结果颜色的存储方式。我们有两种情况:alpha预乘(r、g、b通道乘以alpha通道);非alpha预乘(r、g、b未乘)。您必须确保使用正确的混合公式来使用哪些格式。
当渲染屏幕时,通常使用alpha预乘,它允许使用更简单和更快速的混合公式。
我总结了所有基本情况,但首先提供一些信息:
- 什么是源/目标:如果你按照“Photoshop”的方式思考,你可以将目标视为底层,源视为顶层。
- 混合函数:它是着色器线性插值函数(也称为lerp)。从OpenGL参考文献中可以看到:mix使用A来加权X和Y之间的线性插值:
mix(x, y, a) = x * (1 - a) + y * a
。从我的经验来看,当在GPU片段着色器中使用时,mix
版本比标准公式稍快一些。
- 所有公式都适用于浮点颜色值(从0.0到1.0)
- 如果您想要对源颜色使用不透明度,只需将src.a乘以不透明度即可:
src.a *= opacity
- 在可能出现除以0的公式中,您可能需要添加一个“if”条件,如下所示:
result.a = <formula for alpha>;
if (result.a == 0.0) {
result.rgb = dest.rgb;
} else {
result.rgb = <formula for rgb>;
}
所以这里是公式:
1. 源、目标和结果都未经过预乘处理
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = (dest.rgb * dest.a * (1.0 - src.a) + src.rgb * src.a) / result.a; // division by 0
或者使用混合函数:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = mix(dest.rgb * dest.a, src.rgb, src.a) / result.a; // division by 0!!!!
OpenGL混合函数:
not possible
2. 源、目标和结果都是预乘的
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb;
混合版本:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb;
OpenGL混合功能:
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
3. 目标,结果预乘,源未预乘
result.a = dest.a * (1.0 - src.a) + src.a;
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb * src.a;
混合版本:
result.a = mix(dest.rgb, 1.0, src.a);
result.rgb = mix(dest.rgb, src.rgb, src.a);
OpenGL混合函数:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
4. 目标是不透明的(dest.a = 1.0),所以结果也是不透明的,源图像非预乘
result.rgb = dest.rgb * (1.0 - src.a) + src.rgb * src.a;
result.a = 1.0;
混合版本:
result.rgb = mix(dest.rgb, src.rgb, src.a);
result.a = 1.0;
OpenGL混合函数:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);