在高级三维建模中都少不了Alpha,它作为RGBA颜色模型中的一个分量,表征了物体的透明度。和RGB取值范围一样,Alpha取值从0.0到1.0。 0.0表示完全透明,1.0表示完全不透明。
鉴于Alpha的性质,它可以用于模拟玻璃、水。也可以混合合成图像,对几何图元进行反走样处理。
混合是OpenGL提供的基于像素级的颜色操作,它只支持RGBA颜色模式。在颜色索引表模式下,混合是无效的。
混合是将像素和片元融合起来产生一个新的像素颜色。如果片元是处在混合状态的,那么OpenGL先从帧缓冲中读出像素的颜色,然后和片元的颜色混合,再写回帧缓冲中。
启动和禁用混合效果需要使用glEnable(GL_BLEND)和glDisable(GL_BLEND)。OpenGL缺省状态为禁用混合。
针对混合,片元和像素均有一个因子来控制各自对最终像素颜色的贡献。可以使用glBlendFunc来设置这些因子。glBlendFunc指定了像素混合计算方法,其原型如下:
void glBlendFunc(
GLenum sfactor,
GLenum dfactor
);
sfactor参数表示红、绿、蓝、Alpha混合源值的计算方式,共有9种方式
GL_ZERO
GL_ONE
GL_DST_COLOR
GL_ONE_MINUS_DST_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA
GL_SRC_ALPHA_SATURATE
dfactor参数表示红、绿、蓝、Alpha混合目标值的计算方式,共有8种方式
GL_ZERO
GL_ONE
GL_SRC_COLOR
GL_ONE_MINUS_SRC_COLOR
GL_SRC_ALPHA
GL_ONE_MINUS_SRC_ALPHA
GL_DST_ALPHA
GL_ONE_MINUS_DST_ALPHA
glBlendFunc()定义了混合操作的方式,sfactor 参数指定了用于缩放源颜色值的方法,dfactor 参数指定了用于缩放目标颜色值的方法,共有11种可能,每一种方式都定义了4个缩放因子,分别对应红色、绿色、蓝色和Alpha分量。
假设颜色的源值和目标值分别是(RS,GS,BS,AS)和(Rd,Gd,Bd,Ad),其颜色数目是一个整数,为(kR,kG,kR,kA),其中
kR = 2mR – 1
kG = 2mG – 1
kB = 2mB – 1
kA = 2mA – 1
其中(mR ,mG ,mB ,mA) 是红、绿、蓝、Alpha的颜色位数,如256色的8位、16M色的16位,真彩色的24位和32位等。
如果用(sR ,sG ,sB ,sA) 和(dR ,dG ,dB ,dA)分别表示源因子和目标因子,则下表9-2列出了源因子或目标因子的取值,所有的因子取值范围均是[0,1]。
表9-2 混合方式
源因子/目标因子 |
(f (R) ,f (G) ,f (B) ,f (A) ) |
GL_ZERO |
(0,0,0,0) |
GL_ONE |
(1,1,1,1) |
GL_SRC_COLOR |
(RS/kR,GS/kG,BS/kB,AS/kA) |
GL_ONE_MINUS_SRC_COLOR |
(1,1,1,1) - (RS/kR,GS/kG,BS/kB,AS/kA) |
GL_DST_COLOR |
(Rd/kR,Gd/kG,Bd/kB,Ad/kA) |
GL_ONE_MINUS_DST_COLOR |
(1,1,1,1) |
GL_SRC_ALPHA |
(Rd/kR,Gd/kG,Bd/kB,Ad/kA) - (AS/kA,AS/kA,AS/kA,AS/kA) |
GL_ONE_MINUS_SRC_ALPHA |
(1,1,1,1) - (AS/kA,AS/kA,AS/kA,AS/kA) |
GL_DST_ALPHA |
(AD/kA,AD/kA,AD/kA,AD/kA) |
GL_ONE_MINUS_DST_ALPHA |
(1,1,1,1) - (AD/kA,AD/kA,AD/kA,AD/kA) |
GL_SRC_ALPHA_SATURATE |
(i,i,i,1) |
在表9-2中
i = min (AS,kA– AD) / /kA
在RGB模式下,OpenGL使用下面的公式计算像素的混合RGBA值。
R (d) = min(kR,RssR+RddR)
G (d) = min(kG,GssG+GddG)
B (d) = min(kB,BssB+BddB)
A (d) = min(kA,AssA+AddA)
事实上,混合算法并不是很精确,因为混合操作使用的是整数颜色值。为了简便起见,通常使用(RssR+RddR, GssG+GddG, BssB+BddB, AssA+AddA)来作为混合后的颜色值。例如,针对由远及近绘制的的物体,实现透明效果可以使用glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA),需要注意的是,透明度的计算不需要用到帧缓冲中的Alpha分量。
如果需要实现点线任意方向的反走样,可以使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 。使用glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE)还可以对多边形进行反走样处理。在进行多边形反走样处理前还必须使用glEnable(GL_POLYGON_SMOOTH)启用多边形的光滑绘制。
为了达到正确混合,目标Alpha位必须有效,否则混合无法实现。源Alpha还可以看作是材质的不透明性,范围从1.0到0.0,表示完全不透明到完全透明。
下面我们来对纹理贴图的立方体进行混合处理。
//global variant
GLfloat z = -10.0f; //立方体在z轴的距离
BOOL bBlend = TRUE; //是否使用混合的标志
int glInit()
{
// 启用纹理映射
glEnable(GL_TEXTURE_2D);
//启用阴影平滑(Smooth Shading)。
glShadeModel(GL_SMOOTH);
//背景色,alpha为半透明
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
//设置深度缓冲
glClearDepth(1.0f);
//启动深度测试
glEnable(GL_DEPTH_TEST);
//深度测试的类型
glDepthFunc(GL_LEQUAL);
//进行透视修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
//以baby.bmp文件创建一个纹理
g_Texture[0] = CreateTexture("baby.bmp");
if(!g_Texture[0]) //创建失败则返回
{
MessageBox(g_hWnd, "创建纹理失败!", "提示", MB_OK);
return FALSE;
}
//启动雾化效果
glEnable(GL_FOG);
float fogColor[4] = {0.5f, 0.5f, 0.5f, 1.0f};
glFogi(GL_FOG_MODE, GL_EXP2);
glFogfv(GL_FOG_COLOR, fogColor);
glFogf(GL_FOG_DENSITY, 0.1f);
// 雾的计算精度
glHint(GL_FOG_HINT, GL_DONT_CARE);
glFogf(GL_FOG_START, 0);
glFogf(GL_FOG_END, 30.0f);
//启动混合
glEnable(GL_BLEND);
//设置混合颜色
glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
return TRUE;
}
void glMain()
{
static int angle =0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glTranslatef(0.0f, 0.0f, z);
glRotated(angle, 1, 1, 1);
glFogi(GL_FOG_MODE, fogMode[fog]);
if(!bFog)
glDisable(GL_FOG);
else
glEnable(GL_FOG);
//检查混合标志
if(!bBlend)
{
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
else
{
glEnable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
}
glBindTexture(GL_TEXTURE_2D, g_Texture[0]); // 选择纹理
DrawCube();
angle++;
SwapBuffers(g_hDC);
}
对是否启用混合的控制还是在WindProc()中。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_ACTIVATE:
{
if (!HIWORD(wParam))
{
g_bActive=TRUE;
}
else
{
g_bActive=FALSE;
}
return 0;
} // 监视窗口激活消息
case WM_SIZE:
{
glSceneResize(LOWORD(lParam),HIWORD(lParam));
return 0;
}
case WM_KEYDOWN:
switch(wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
return 0;
case VK_SPACE:
if(fog==2)
fog=0;
else
fog++;
break;
case VK_F1:
bFog= !bFog;
break;
case VK_F2: //F2键切换是否使用混合
bBlend=!bBlend;
break;
case VK_UP: //向上方向键拉近立方体和观察者的距离
z+=0.2;
break;
case VK_DOWN: //向下方向键拉远立方体和观察者的距离
z-=0.2;
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
程序运行后,可以使用F2键来切换是否使用混合,按向上的方向键和向下的方向键则改变立方体和观察者的距离,雾的效果仍存在有效,如果9-2所示。