1.混合概念
混合就是把两种颜色混在一起。具体一点就是,把某一像素位置原来的颜
色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊效果。
使用OpenGL 混合功能 :glEnable(GL_BLEND);
关闭OpenGL混合功能 : glDisable(GL_BLEND);
注意: RGBA 模式下,可以使用混合功能,颜色索引模式下无法使用
2. 源原因子和目标因子
混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理
后得到一种新的颜色。 将要画上去的颜色为源颜色,原来的颜色称为
目标颜色。
OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘
以的系数为 源因子,目标颜色乘以的系数称为 目标因子),然后相加的
到新的颜色
数学公式表示:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
源颜色的四个分量(指红色,绿色,蓝色,alpha 值)是(Rs, Gs, Bs,
As)
目标颜色的四个分量是(Rd, Gd, Bd, Ad)
源因子为(Sr, Sg, Sb, Sa)
目标因子为(Dr, Dg, Db, Da)。
源因子和目标因子可以通过 glBlendFunc 函数来进行设置。
两个参数:
1. 源因子
2. 目标因子
参数种类:
GL_ZERO: 使用0.0作为因子 实际上 不使用这种颜色参与运算
GL_ONE: 用1.0作为因子 实际上相当于完全使用这种颜色参与运算
GL_SRC_ALPHA: 使用源颜色的alpha 值作为因子
GL_DST_ALPHA: 表示使用目标颜色的 alpha 值来作为因子
GL_ONE_MINUS_SRC_ALPHA: 表示用1.0减去源颜色的 alpha 值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用 1.0 减去目标颜色的 alpha 值来作为因子。
注意:
源颜色和目标颜色,是和绘制的顺序有关的。比如先绘制一个红色的
物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色
。若反过来,则红色就是源颜色,绿色才是目标颜色。因此,绘制注
意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的
目标因子对应。
3. 二维图形混合举例
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);
// 使用混合功能
glEnable(GL_BLEND);
// glBlendFunc 设置混合因子
// GL_ONE 源因子1.0
// GL_ZERO 目标因子0
glBlendFunc(GL_ONE,GL_ZERO);
glColor4f(1,0,0,0.5);
gRectf(-1,-1,0.5,0.5);
glColor4f(0,1,0,0.5);
glRectf(-0.5,-0.5,1,1);
glutSwapBuffers();
}
4. 三维混合
深度缓冲区:
它是一段数据,它记录了每一个像素距离观察者有多近。
启动深度缓冲测试情况下:如果将要绘制的像素比原来的像素更近,
则像素将被绘制,否则,像素就会被忽略掉,不进行绘制。
实现半透明效果的问题:
绘制了一个近距离的半透明物体,则它在深度缓冲区保留了一些信息
,使得远处的物体将无法再被绘制出来,虽然半透明的物体仍然半
透明,但透过它看到的却不是正确的内容了。
解决办法:
需要在绘制半透明物体时将深度缓冲区设置为只读,这样,虽然透明
物体被绘制上去,但是深度缓冲区还保持原来的状态。
如果再有一个物体出现在半透明物体之后,在不透明物体之前,则它
可以被绘制,因为此时深度缓冲区记录的那个是不透明无的深度。
以后再绘制不透明物体时候,只需要在将深度缓冲区设置为可读可写
形式即可。
一部分透明粉i一部分不透明的物体,只需要把物体分为两个部分,
一部分全是半透明的,一部分全是不透明的。
绘制原则:
必须先绘制不透明的物体,然后绘制透明的物体。
假设 背景为蓝色, 近处一块红色玻璃,中间一个绿色物体,
如果先绘制红色半透明玻璃,它先和蓝色背景混合,则以后绘制中
间的绿色物体,当单独于红色玻璃混合已经不能实现了。
绘制顺序:
1.先绘制所有不透明的物体。若两个物体都是不透明的,则先后顺序
没有关系。
2..然后将深度缓冲区设置为只读,绘制所有半透明的物体,若两个物
体都是半透明的,则先后根据自己的意愿(注意,先绘制的将成
为目标颜色,后绘制的将成为源颜色,绘制顺序将会对结果造成一
些影响,最后将深度缓冲区设置为可读可写形式。)
深度缓冲区设置只读:
glDepthMask(GL_FALSE);
深度缓冲区设置可读写:
glDepthMask(GL_TRUE)
示例代码
// 设置光源
void setLight(void)
{
static const GLfloat light_position[] = {1.0f, 1.0f, -1.0f, 1.0f};
static const GLfloat light_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
static const GLfloat light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f};
static const GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
// 设置材质
void setMatirial(const GLfloat mat_diffuse[4], GLfloat mat_shininess)
{
static const GLfloat mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);
}
void myDisplay(void)
{
// 定义一些材质颜色
const static GLfloat red_color[] = {1.0f, 0.0f, 0.0f, 1.0f};
const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f};
const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f};
// 清除屏幕
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 启动混合并设置混合因子
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// 设置光源
setLight();
// 以(0, 0, 0.5)为中心,绘制一个半径为.3 的不透明红色球体(离观察者最远)
setMatirial(red_color, 30.0);
glPushMatrix();
glTranslatef(0.0f, 0.0f, 0.5f);
glutSolidSphere(0.3, 30, 30);
glPopMatrix();
// 下面将绘制半透明物体了,因此将深度缓冲设置为只读
glDepthMask(GL_FALSE);
// 以(0.2, 0, -0.5)为中心,绘制一个半径为.2 的半透明蓝色球体(离观察者最近)
setMatirial(blue_color, 30.0);
glPushMatrix();
glTranslatef(0.2f, 0.0f, -0.5f);
glutSolidSphere(0.2, 30, 30);
glPopMatrix();
// 以(0.1, 0, 0)为中心,绘制一个半径为.15 的半透明绿色球体(在前两个球体之间)
setMatirial(green_color, 30.0);
glPushMatrix();
glTranslatef(0.1, 0, 0);
glutSolidSphere(0.15, 30, 30);
glPopMatrix();
// 完成半透明物体的绘制,将深度缓冲区恢复为可读可写的形式
glDepthMask(GL_TRUE);
glutSwapBuffers();
}
结果如图: