- 理论基础
混合:主要就是用来做一些如透明的特效,其实就是源颜色与目标颜色进行混合计算得到一个新颜色的过程。混合发生在图元光栅化之后,片段写入帧缓冲之前,片段与对应位置的帧缓冲区像素进行互操作形成新的像素颜色的过程及时混合。需要通过glEnable(GL_BLEND)来激活混合功能,否则的话片段直接覆盖对应像素。颜色的alpha分量值此时就会发挥作用。
默认混合计算得到的新颜色是:
注释:
1,先写入帧缓冲的是目标颜色,后将要写入的是源颜色,与它们的深度值大小无关。
2,openGL渲染时,将颜色值放在颜色缓冲区中,将深度值放在深度缓冲区中,如果深度测试关闭时,新的颜色值就会简单的覆盖颜色缓冲区中原来的值,而如果深度测试开启时,只有新的颜色深度值大于原来的值时才被覆盖。所以一般情况下颜色值要么完全覆盖要么完全丢弃。而如果同时又开启了混合,那么片段值就不能随便丢弃或覆盖了,这时主要就是要注意绘制的顺序,而如果这个绘制顺序很难确定,可以用glDepthMask(GL_FALSE)设置深度缓冲区为只读状态,这样来限制受深度测试的影响。
3,绘制顺序:对于半透明的,要先画远,再画近。对于不透明的,因为有深度测试,顺序不影响最终效果。但要优化性能的话,一般是先画近,再画远(直接丢弃而不需要覆盖)。苹果手机的显卡,对于不透明的,关闭混合之后,可以不论远近,性能一样。
- 实例代码
1,背景颜色与几何物体颜色做基本混合
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
void init(void)
{
glClearColor(1.0, 1.0, 0.0, 0.0);
glBlendFunc(GL_ONE, GL_ONE);//设置源因子与目标因子取值
glEnable(GL_BLEND);//开启混合
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//这个方形与背景混合
glColor3f(0.0, 0.0, 1.0);
glRectf(-0.5,-0.5,0.5,0.5);
glFlush();
}
void reshape(int w, int h)
{
GLdouble aspect = (GLdouble) w / h;
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (aspect < 1.0) {
aspect = 1.0 / aspect;
glOrtho(-aspect, aspect, -1.0, 1.0, -1.0, 1.0);
} else
glOrtho(-1.0, 1.0, -aspect, aspect, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
}
void keyboard(unsigned char key, int x, int y)
{
//设置混合的计算方式
switch (key) {
case 'a': case 'A':
//源颜色+目标颜色(默认形式)
glBlendEquation(GL_FUNC_ADD);
break;
case 's': case 'S':
//源颜色-目标颜色
glBlendEquation(GL_FUNC_SUBTRACT);
break;
case 'r': case 'R':
//目标颜色-源颜色
glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
break;
case 'm': case 'M':
//分别取目标颜色和源颜色rgba中较小的值
glBlendEquation(GL_MIN);
break;
case 'x': case 'X':
//分别取目标颜色和源颜色rgba中较大的值
glBlendEquation(GL_MAX);
break;
case 27:
exit(0);
}
glutPostRedisplay();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(512,512);
glutInitWindowPosition(100, 100);
glutCreateWindow(argv[0]);
init();
glewInit();
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
2,利用混合做遮挡透明
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
static int leftFirst = GL_TRUE;
static void init(void)
{
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel (GL_FLAT);
glClearColor (0.0, 0.0, 0.0, 0.0);
}
static void drawLeftTriangle(void)
{
glBegin (GL_TRIANGLES);
glColor4f(1.0, 1.0, 0.0, 0.75);
glVertex3f(0.1, 0.9, 0.0);
glVertex3f(0.1, 0.1, 0.0);
glVertex3f(0.7, 0.5, 0.0);
glEnd();
}
static void drawRightTriangle(void)
{
glBegin (GL_TRIANGLES);
glColor4f(0.0, 1.0, 1.0, 0.75);
glVertex3f(0.9, 0.9, 0.0);
glVertex3f(0.3, 0.5, 0.0);
glVertex3f(0.9, 0.1, 0.0);
glEnd();
}
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
if (leftFirst) {
drawLeftTriangle();//目标颜色
drawRightTriangle();//源颜色
}
else {
drawRightTriangle();
drawLeftTriangle();
}
glFlush();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
gluOrtho2D (0.0, 1.0, 0.0, 1.0*(GLfloat)h/(GLfloat)w);
else
gluOrtho2D (0.0, 1.0*(GLfloat)w/(GLfloat)h, 0.0, 1.0);
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 't':
case 'T':
leftFirst = !leftFirst;
glutPostRedisplay();
break;
case 27: /* Escape key */
exit(0);
break;
default:
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize (200, 200);
glutCreateWindow (argv[0]);
init();
glutReshapeFunc (reshape);
glutKeyboardFunc (keyboard);
glutDisplayFunc (display);
glutMainLoop();
return 0;
}
3,使用深度缓冲区进行三维混合(主要是使用glDepthMask来控制不让深度测试丢弃遮挡部分颜色值,然后就可以和基本操作一样进行混合)
#include "GLTools.h"
#include "GLShaderManager.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
#define MAXZ 8.0
#define MINZ -8.0
#define ZINC 0.4
static float solidZ = MAXZ;
static float transparentZ = MINZ;
static GLuint sphereList, cubeList;
static void init(void)
{
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 0.15 };
GLfloat mat_shininess[] = { 100.0 };
GLfloat position[] = { 0.5, 0.5, 1.0, 0.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
//使用显示列表绘制球体
sphereList = glGenLists(1);
glNewList(sphereList, GL_COMPILE);
glutSolidSphere (0.4, 16, 16);
glEndList();
//使用显示列表绘制立方体
cubeList = glGenLists(1);
glNewList(cubeList, GL_COMPILE);
glutSolidCube (0.6);
glEndList();
}
void display(void)
{
GLfloat mat_solid[] = { 0.75, 0.75, 0.0, 1.0 };
GLfloat mat_zero[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat mat_transparent[] = { 0.0, 0.8, 0.8, 0.6 };
GLfloat mat_emission[] = { 0.0, 0.3, 0.3, 0.6 };
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix ();
glTranslatef (-0.15, -0.15, solidZ);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_zero);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_solid);
glCallList (sphereList);
glPopMatrix ();
glPushMatrix ();
glTranslatef (0.15, 0.15, transparentZ);
glRotatef (15.0, 1.0, 1.0, 0.0);
glRotatef (30.0, 0.0, 1.0, 0.0);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_transparent);
glEnable (GL_BLEND);
glDepthMask (GL_FALSE);//设置深度缓冲区只读(目的是不让它丢弃遮挡部分)
glBlendFunc (GL_SRC_ALPHA, GL_ONE);
glCallList (cubeList);
glDepthMask (GL_TRUE);//恢复深度缓冲区读写
glDisable (GL_BLEND);
glPopMatrix ();
glutSwapBuffers();
}
void reshape(int w, int h)
{
glViewport(0, 0, (GLint) w, (GLint) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void animate(void)
{
if (solidZ <= MINZ || transparentZ >= MAXZ)
glutIdleFunc(NULL);
else {
solidZ -= ZINC;
transparentZ += ZINC;
glutPostRedisplay();
}
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 'a':
case 'A':
solidZ = MAXZ;
transparentZ = MINZ;
glutIdleFunc(animate);
break;
case 'r':
case 'R':
solidZ = MAXZ;
transparentZ = MINZ;
glutPostRedisplay();
break;
case 27:
exit(0);
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(500, 500);
glutCreateWindow(argv[0]);
init();
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutDisplayFunc(display);
glutMainLoop();
return 0;
}