实践项目:https://github.com/JackieLong/OpenGL/tree/main/project_blend_test
混合是实现物体透明度(Transparency)的一种技术,一个透明的物体的颜色是物体本身的颜色和它背后的物体颜色的不同强度结合。用数学公式表示如下:
glBlendFunc
// C: 物体最终颜色
// C_src: 物体本身颜色
// C_dst: 颜色缓冲区对应位置颜色(物体后面的颜色)
// C_x: 表示C_src或C_dst
// alpha_src: 物体alpha值
// alpha_dst: 颜色缓冲区alpha值
// alpha_x: 表示alpha_src或alpha_dst
// C_cstnt: 某一个常量颜色,可以通过函数设置
// alpha_cstnt: 某一个常量alpha值,可以通过函数设置
// 设置混合因子。
// S:源混合因子,即源颜色乘以的那个因子
// D:目标混合因子,即目标颜色乘以的那个因子
glBlendFunc( GLenum S, GLenum D );
// S、D可选值 RGB混合 Alpha混合
// GL_ZERO C_x x 0 alpha_x x 0
// GL_ONE C_x x 1 alpha_x x 1
// GL_SRC_COLOR C_x x C_src alpha_x x alpha_src
// GL_DST_COLOR C_x x C_dst alpha_x x alpha_dst
// GL_SRC_ALPHA C_x x alpha_src alpha_x x alpha_src
// GL_DST_ALPHA C_x x alpha_dst alpha_x x alpha_dst
// GL_CONSTANT_COLOR C_x x C_cstnt alpha_x x alpha_cstnt
// GL_CONSTANT_ALPHA C_x x alpha_cstnt alpha_x x alpha_cstnt
// GL_ONE_MINUS_SRC_COLOR C_x x (1 - C_src) alpha_x x (1 - alpha_src)
// GL_ONE_MINUS_DST_COLOR C_x x (1 - C_dst) alpha_x x (1 - alpha_dst)
// GL_ONE_MINUS_SRC_ALPHA C_x x (1 - alpha_src) alpha_x x (1 - alpha_src)
// GL_ONE_MINUS_DST_ALPHA C_x x (1 - alpha_dst) alpha_x x (1 - alpha_dst)
// GL_ONE_MINUS_CONSTANT_COLOR C_x x (1 - C_cstnt ) alpha_x x (1 - alpha_cstnt)
// GL_ONE_MINUS_CONSTANT_ALPHA C_x x (1 - C_cstnt) alpha_x x (1 - alpha_cstnt)
// GL_SRC_ALPHA_SATURATE C_x x min(alpha_src, 1 - alpha_dst) alpha_x 1
// ************************ 例子
glBlendFunc(GL_ZERO, GL_ONE); // C = C_src * 0 + C_dst * 1
glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); // C = C_src * C_dst + C_dst * C_src
glBlendColor(r, g, b, a); // 设置C_cstnt、alpha_cstnt
glBlendFunc(GL_CONSTANT_COLOR, GL_CONSTANT_ALPHA); // C = C_src * C_cstnt + C_dst * alpha_cstnt
glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA); // C = C_src * alpha_dst + C_dst * alpha_src
// 最常见的混合
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // C = C_src * alpha_src + C_dst * (1 - alpha_src)
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
// C = C_src * (1 - C_dst) + C_dst * (1 - C_src)
glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_SRC_ALPHA_SATURATE);
// C = C_src * min(alpha_src, 1 - alpha_dst) + C_dst * min(alpha_src, 1 - alpha_dst)
glBlendFuncSeparate
通过设置,是把混合因子中的RGB因子和alpha因子绑定到一起了。即RGBA因子,并不能分开分别设置,可以满足这个需求:
glBlendFuncSeparate(GLenum srcRGB, // 源rgb混合因子,取值同上
GLenum dstRGB, // 目标rgb混合因子,取值同上
GLenum srcAlpha, // 源alpha混合因子,取值同上
GLenum dstAlpha); // 目标alpha混合因子,取值同上
glBlendEquation
设置上面的运算符。
void glBlendEquation( GLenum mode );
// 设置混合方程式,mode可选值如下:
// GL_FUNC_ADD(default) C = C_src * F_src + C_dst * F_dst
// GL_FUNC_SUBTRACT C = C_src * F_src - C_dst * F_dst
// GL_FUNC_REVERSE_SUBTRACT C = C_dst * F_dst - C_src * F_src
// GL_MIN C = min(C_src, C_dst)
// GL_MAX C = max(C_src, C_dst)
// 一般不用设置,按默认设置即可满足大部分需求。
常见问题
混合是将颜色缓冲区片段和新生成的片段颜色混合。深度测试是决定是否要丢弃新生成的片段。
而且深度测试是发生在混合之前的,倘若深度测试要丢失新生成的片段,则导致混合中没有新片段数据,最终只会保留颜色缓冲区的值。
重现场景:如下图,数字表示物体的绘制顺序,当绘制窗户4时,深度测试发现3窗口的深度值大于4,丢失了3和4重叠的部分,导致混合效果异常。
解决办法:手动调整绘制顺序,先绘制“远”的物体,再绘制近的物体。(按距离摄像机的距离从大到小排序)。