OpenGL超级宝典学习笔记——混合


混合

在正常情况下,OpenGL渲染时会把颜色值输入到颜色缓冲区中,深度值输入到深度缓冲区中。如果我们关闭深度测试,那么新的颜色值会简单地覆盖已经存在于颜色缓冲区中的值。当开启深度测试时,颜色段只有在通过深度测试时,才会覆盖已经存在于颜色缓冲区中的值。在这两种情况下,在渲染时颜色值要么完全被废弃,要么就直接覆盖旧的颜色值。现在介绍一种新的方式,混合。

glEnable(GL_BLEND);

当开启混合时,输入的颜色值将会和已经存在于颜色缓冲区中的颜色进行组合。至于如何进行组合,取决于你为混合设置的参数。

 

组合颜色

首先需要了解两个概念:

  • 目标颜色:已经存在于颜色缓冲区中的颜色值。这个颜色包含红、绿、蓝三种成分,和可选的alpha值。
  • 源颜色:来自于渲染命令的颜色。包含红、绿、蓝和可选的alpha值成分。

当开启OpenGL的混合时,源颜色是如何与目标颜色进行结合的,取决于你设置的混合方程式。默认情况下的,混合方程如下:

Ci = (Cs * S) + (Cd * D)

在这里,Ci是最终被计算出来的颜色,Cs是源颜色,Cd是目标颜色。而S和D分别是源混合因子和目标混合因子。这些混合因子通过:glBlendFunc(GLenum S, GLenum D);设置。S和D是枚举值,其枚举列表如下:(R、G、B、A分别代表红色成分,绿色成分,蓝色成分,Alpha成分)

Function RGB Blend Factors Alpha Blend Factor
GL_ZERO

(0,0,0)

0
GL_ONE (1,1,1) 1
GL_SRC_COLOR (Rs ,Gs ,Bs) As
GL_ONE_MINUS_SRC_COLOR (1,1,1)-(Rs,Gs ,Bs) 1-As
GL_DST_COLOR (Rd,Gd ,Bd) Ad
GL_ONE_MINUS_DST_COLOR (1,1,1)-(Rd,Gd ,Bd) 1-Ad
GL_SRC_ALPHA (As ,As,As ) As
GL_ONE_DST_ALPHA (Ad ,Ad,Ad ) Ad
GL_ONE_MINUS_DST_ALPHA (1,1,1)-(Ad ,Ad,Ad ) 1-Ad
GL_CONSTANT_COLOR (Rc,Gc,Bc) Ac
GL_ONE_MINUS_CONST_COLOR (1,1,1)-(Rc,Gc,Bc) 1-Ac
GL_CONST_ALPHA (Ac,Ac,Ac) Ac
GL_ONE_MINUS_CONSTANT_ALPHA (1,1,1)-(Ac,Ac,Ac) 1-Ac
GL_SRC_ALPHA_SATURATE (f,f,f)* 1

 

* f = min(A s , 1 – A d)

PS:上面表格的颜色值都是用浮点数来表示的。举例说明:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

通过上面的调用来告诉OpenGL我们使用的混合因子。假设现在渲染命令传进来的源RGBA颜色值是(1.0f,0.0f,1.0f,1.0f).而在颜色缓冲区相同的位置已经有目标颜色RGBA值是(0.0f, 1.0f, 0.0f, 1.0f).

代入公式:Ci = (Cs * S) + (Cd * D)

Cs=(1.0f,0.0f,1.0f,0.6f), GL_SRC_ALPHA参数说明源混合因子取源颜色的alpha值,所以 S = 0.6f。Cd=(0.0f, 1.0f, 0.0f, 1.0f). GL_ONE_MINUS_SRC_ALPHA说明了目标混合因子取1-As,D = 1-0.6 = 0.4f;

所以代入计算得(Cs * S) = (0.6f, 0.0f, 0.6f, 0.36f) , (Cd * D) = (0.0f, 0.4f, 0.0f, 0.4f)

Ci = (Cs * S) + (Cd * D) = (0.6f, 0.4f, 0.6f, 0.76f)

得到的颜色是近似于洋紫色。

上面表格的GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA和GL_ONE_MINUS_CONSTANT_ALPHA的值在默认情况下是(0.0f, 0.0f, 0.0f, 0.0f)但我们可以通过下面的函数修改:

void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);

颜色混合经常用于在不透明的前面画一个透明的物体。这样我们就可以先画好背景,然后再在上面画一个半透明的物体。可以制造一种,透过彩色玻璃看外面的风景的效果。下面的例子:(物体在光滑的彩色地面上的倒影)。

#include "gltools.h"
#include "math3d.h"

//光源位置

GLfloat lightPos[4] = { -100.0f, 100.0f, 50.0f, 1.0f };
GLfloat lightPosMirror[4] = {-100.0f, -100.0f, 50.0f, 1.0f};

GLfloat fNoLight[] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat fBrightLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

void DrawGround()
{
//画地面

GLfloat fExtent = 20.0f;
GLfloat fStep = 0.5f;
GLfloat y = 0.0f;
GLfloat fColor;
GLfloat iStrip, iRun;
GLint iBounce = 0;

glShadeModel(GL_FLAT);
for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
{
glBegin(GL_TRIANGLE_STRIP);
for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
{
if((iBounce %2) == 0)
fColor = 1.0f;
else
fColor = 0.0f;

glColor4f(fColor, fColor, fColor, 0.5f);
glVertex3f(iStrip, y, iRun);
glVertex3f(iStrip + fStep, y, iRun);

iBounce++;
}
glEnd();
}
glShadeModel(GL_SMOOTH);
}

void DrawWorld()
{
glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix();

glTranslatef(0.0f, 0.5f, -3.5f);
gltDrawTorus(0.25, 0.08, 68, 37);

glPopMatrix();
}

void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glPushMatrix();
//设置光源的倒影
glLightfv(GL_LIGHT0, GL_POSITION, lightPosMirror);

glPushMatrix();
//因为是镜像,所以要反过来绘制
glFrontFace(GL_CW);
glScalef(1.0f, -1.0f, 1.0f);
DrawWorld();
glFrontFace(GL_CCW);
glPopMatrix();
//画地面时,关闭光源, 地面可见并均匀着色。
glDisable(GL_LIGHTING);
//打开混合,设置混合因子
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//画地面
DrawGround();
//关闭混合效果
glDisable(GL_BLEND);
glEnable(GL_LIGHTING);

glPopMatrix();
//设置光源在左上角
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
DrawWorld();

glutSwapBuffers();
}


void ChangeSize(GLsizei w, GLsizei h)
{
if (h == 0)
h = 1;

glViewport(0, 0, w, h);

GLfloat faspect = (GLfloat)w/(GLfloat)h;

glMatrixMode(GL_PROJECTION);
glLoadIdentity();

gluPerspective(35.0, faspect, 1.0f, 50.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//往Y轴负方向平移一点
glTranslatef(0.0f, -0.4f, 0.0f);
glutPostRedisplay();

}

void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//开启深度测试,剔除物体背面
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
//逆时针方向绘制的面为正面
glFrontFace(GL_CCW);

//开启光照,设置光源参数
glEnable(GL_LIGHTING);
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
glEnable(GL_LIGHT0);

//开启颜色追踪,设置镜面加亮效果
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMateriali(GL_FRONT, GL_SHININESS, 128);

}

int main(int args, char **argv)
{
glutInit(&args, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("Reflection");
glutDisplayFunc(RenderScene);
glutReshapeFunc(ChangeSize);
SetupRC();
glutMainLoop();
return 0;
}

image

 

改变混合方程

OpenGL的默认混合方程式:Ci = (Cs * S) + (Cd * D)

但我们可以通过函数:

void glBlendEquation(GLenum mode);

来改变混合方程。可选择的混合方程模式如下表:

image

还有一个更灵活的函数:

void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);

这个函数允许你分别指定RGB的混合方程和alpha成分的混合方程。

OpenGL超级宝典 第4版 中文版PDF+英文版+源代码 见 

OpenGL编程指南(原书第7版)中文扫描版PDF 下载

OpenGL 渲染篇

Ubuntu 13.04 安装 OpenGL

OpenGL三维球体数据生成与绘制【附源码】

Ubuntu下OpenGL编程基础解析

如何在Ubuntu使用eclipse for c++配置OpenGL  

更多《OpenGL超级宝典学习笔记》相关知识 见 

相关内容