混合简介
混合在OpenGL的多图层叠加和缓冲区颜色计算中必不可少。开发人员可以通过OpenGL自带的混合接口进行混合,也可以在着色器中自定义相关混合算法进行融合。本文将详细讲解OpenGL自带的混合算法的计算方法,帮助需要进行颜色混合处理操作的开发人员快速了解混合算法中的各项含义和计算方法,并简要讲解两种混合方式的实现。
基本概念
- 输入片段的颜色和Alpha通常称为源颜色分量和源Alpha,目标像素的颜色和Alpha则称为目标颜色分量和目标Alpha。在混合过程中,是将新渲染的颜色-输入片段-src(源)与现有的颜色-目标像素-dst(目标)进行混合,通过计算将最终的混合结果呈现在屏幕上。通俗点介绍即源是指新渲染需要混合的颜色,目标是指缓冲区中现有的、将要被混合的颜色。
- 混合方程式为C_final = f_source * C_source op f_destination * C_destination。
- f_source是输入片段(源)的混合因子;
- C_source是输入片段(源)的颜色;
- f_destination是目标像素(目标)的混合因子;
- C_destination是目标像素(目标)的颜色;
- op是混合运算符,可以是GL_FUNC_ADD(前后两项相加),GL_FUNC_SUBTRACT(前后两项相减),GL_FUNC_REVERSE_SUBTRACT(前后两项相减,但顺序相反)。
混合使用
快速使用混合只需要以下四步:
- 启用混合模式
glEnable(GL_BLEND)
; - 设置混合函数
glBlendFunc(GLenum sfactor,GLenum dfactor)
; - 指定混合运算符
glBlendEquation(GLenum mode)
; - 关闭混合模式
glDisable(GL_BLEND)
(在无需使用混合的结尾调用);
再深入使用
- 设置混合函数还可以调用
glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
分别对输入片段和目标片段设置RGB分量和Alpha值的混合因子; - 指定混合运算符还可以通过调用
glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha)
分别指明RGB和Alpha的混合运算符; - 如果混合函数选择了包含常量,如
GL_CONSTANT_COLOR
,则在设置混合参数的时候还需要通过glBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
设置常量颜色作为混合因子进行计算。
混合算法
OpengGL提供了15中混合函数供开发者快速使用,包括(混合系数枚举值、混合因子、注释):
-
GL_ZERO
(0,0,0,0) 混合因子都为0; -
GL_ONE
(1,1,1,1) 混合因子都为1; -
GL_SRC_COLOR
(Rs,Gs,Bs,As) 混合因子为源颜色分量; -
GL_ONE_MINUS_SRC_COLOR
(1-Rs,1-Gs,1-Bs,1-As) 混合因子为1-C_source; -
GL_SRC_ALPHA
(As,As,As,As) 混合因子为源Alpha值; -
GL_ONE_MINUS_SRC_ALPHA
(1-As,1-As,1-As,1-As) 混合因子为1-f_source; -
GL_DST_COLOR
(Rd,Gd,Bd,Ad) 混合因子为目标颜色分量; -
GL_ONE_MINUS_DST_COLOR
(1-Rd,1-Gd,1-Bd,1-Ad) 混合因子为1-C_destination; -
GL_DST_ALPHA
(Ad,Ad,Ad,Ad) 混合因子为目标Alpha值; -
GL_ONE_MINUS_DST_ALPHA
(1-Ad,1-Ad,1-Ad,1-Ad) 混合因子为1-f_destination; -
GL_CONSTANT_COLOR
(Rc,Gc,Bc,Ac) 混合因子为常量颜色; -
GL_ONE_MINUS_CONSTANT_COLOR
(1-Rc,1-Gc,1-Bc,1-Ac) 混合因子为1-常量颜色; -
GL_CONSTANT_ALPHA
(Rc,Gc,Bc,Ac) 混合因子为常量Alpha; -
GL_ONE_MINUS_CONSTANT_ALPHA
(1-Rc,1-Gc,1-Bc,1-Ac) 混合因子为1-常量Alpha; -
GL_SRC_ALPHA_SATURATE
(min(As,1-Ad)) 混合因子为源Alpha和(1-目标Alpha)的最小值;
其中R指的是红色通道、G指的是绿色通道、B指的是蓝色通道、A指的是Alpha、s指的是源(输入片段),d指的是目标(目标像素),c指的是自定义的常量。
以GL_ONE_MINUS_DST_ALPHA
为例,1-Ad指的是1-目标Alpha。
举例计算
例子1,使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR)
在实际运用场景中开启混合模式。先在图层上上绘制一个颜色W(0.3,0.4,0.7,0.5),紧接着再绘制一个颜色X(0.8,0.2,0.3,0.1)。如果采用的混合函数为glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_COLOR)
,运算符为glBlendEquation(GL_FUNC_ADD)
,其混合结果计算如下:
- 源混合函数采用
GL_SRC_ALPHA
,其混合因子是源Alpha(As,As,As,As)。计算方式为将源颜色分别乘上As=0.5,所以混合方程式的第一段f_source * C_source = (As,As,As,As) * W(0.3,0.4,0.7,0.5)=(0.5,0.5,0.5,0.5) * W(0.3,0.4,0.7,0.5) = (0.15,0.2,0.35,0.25); - 目标混合函数采用
GL_ONE_MINUS_SRC_COLOR
,其混合因子是1-C_source。计算方式为将目标颜色分别乘(1-源颜色),所以混合方程式的第二段f_destination * C_destination = (1-Rs,1-Gs,1-Bs,1-As) * X(0.8,0.2,0.3,0.1) = (1-0.8,1-0.2,1-0.3,1-0.1) * X(0.8,0.2,0.3,0.1)=(0.2,0.8,0.7,0.9) * X(0.8,0.2,0.3,0.1) = (0.16,0.16,0.21,0.09); - 根据混合方程式即上述两部分相加,即源(0.15,0.2,0.35,0.25) + 目标(0.16,0.16,0.21,0.09) = C_final(0.31,0.36,0.56,0.34),因此混合结果输出为该点最终显示 C_final(0.31,0.36,0.56,0.34)。
例子2,使用glBlendFuncSeparate(GL_ONE,GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_DST_ALPHA)
条件和例子1中一样,除了混合函数采用glBlendFuncSeparate(GL_ONE,GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_DST_ALPHA)
,通过分解可知
- 源颜色混合函数采用
GL_ONE
,其混合因子是(1,1,1,1)。计算方式为将源颜色分别乘1,所以混合方程式第一段f_source * C_source 中的RGB通道计算为 (1,1,1) * W(0.3,0.4,0.7) = (0.3,0.4,0.7); - 源Alpha混合函数采用
GL_ONE
,其混合因子是(1,1,1,1)。计算方式为将源Alpha乘1,所以混合方程式第一段f_source * C_source 中的Alpha通道计算为(1) * (0.5) =(0.5),所以方程式第一段结果为(0.3,0.4,0.7,0.5); - 目标颜色混合函数采用
GL_ONE_MINUS_SRC_ALPHA
,其混合因子是(1-As,1-As,1-As,1-As),所以混合方程式第二段f_destination * C_destination中的RGB通道计算为 (1-0.5,1-0.5,1-0.5) * X(0.8,0.2,0.3) = (0.5,0.5,0.5) * X(0.8,0.2,0.3) = (0.4,0.1,0.15); - 目标Alpha混合函数采用
GL_ONE_MINUS_DST_ALPHA
,其混合因子是(1-Ad,1-Ad,1-Ad,1-Ad),所以混合方程式第二段f_destination * C_destination中的Alpha通道计算为(1-0.1) * *X(0.1) *= (0.9) * (0.1) = (0.09),所以方程式第二段结果为 (0.4,0.1,0.15,0.09); - 根据混合方程式即上述两部分相加,即源(0.3,0.4,0.7,0.5) + 目标(0.4,0.1,0.15,0.09) = C_final(0.7,0.5,0.85,0.59),因此混合结果输出为该点最终显示C_final(0.7,0.5,0.85,0.59)。
着色器中混合
由于OpenGL自带的混合算法接口存在一定局限性,除了上述介绍的内容就无法自定义扩展,因此开发人员也可以在着色器中完成混合。但这样的做法不仅需要当前帧的颜色缓冲数据作为着色器输入,还需要前一帧的颜色缓冲数据,从而自定义算法完成混合。因此其在时间和空间上存在一定劣势,但换来的是高自由度、可自定义的混合算法。后续的文章中将以例子的形式介绍在着色器中使用混合。