大家好。这一讲,我们讲介绍OpenGL的颜色混合模式。
其实颜色混合用到的场合很多,比如多张图片的合成,动画游戏中的一些画面特效等都可以通过颜色混合进行实现。最常用的混合方式就是实现物体与背景的半透明效果。另外,在制作2D游戏时颜色混合可以用来通过制作目标物体的蒙板实现移动。通过蒙板来消除旧位置的物体对象可以不必重绘当前整帧内容,而仅仅是发生变化的那些物体。下面给出示例代码:
// // MyView.m // OpenGLTest // // Created by Zenny Chen on 4/25/10. // Copyright 2010 GreenGames Studio. All rights reserved. // P44 #import "MyView.h" #include <OpenGL/OpenGL.h> #include <math.h> @implementation MyView - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { // Initialization code here. } return self; } // Destination: a rectangle static c*****t struct VertexInfo { GLfloat vertices[3]; }vertexList[] = { {-0.5f, 0.5f, -1.0f}, {-0.5f, -0.5f, -1.0f}, {0.5f, 0.5f, -1.0f}, {0.5f, -0.5f, -1.0f} }; - (void)prepareOpenGL { glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertexList); glFrontFace(GL_CCW); glCullFace(GL_BACK); glShadeModel(GL_SMOOTH); // Set Background color(frame buffer color) glClearColor(1.0, 0.0, 0.0, 1.0); glViewport(0, 0, 320, 320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, 5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // destination color glColor4f(1.0f, 1.0f, 1.0f, 0.3f); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA); } - (void)drawRect:(NSRect)dirtyRect { // Drawing code here. glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glFlush(); } @end
上述代码第67行的glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA);用于指定:
源混合因子:(As, As, As, As),这里是(0.3, 0.3, 0.3, 0.3)
目标混合因子则是:(1, 1, 1, 1) - (As, As, As, As),结果是(0.7, 0.7, 0.7, 0.7)。
这里我们能够看到,为目标和源指定混合因子可以是源、目标的任一颜色分量或alpha因子,或是[0, 1]范围内的补。
对于上述代码,我们可以用等价的方式实现。
已知,目的像素颜色值为:(1.0, 0.0, 0.0, 1.0);源像素颜色值为:(1.0f, 1.0f, 1.0f, 0.3f)。
目的混合因子为:(0.7, 0.7, 0.7, 0.7);源混合因子为:(0.3, 0.3, 0.3, 0.3)
OK。我们可以直接把最终目标像素颜色值给算出来——(1.0 * 0.3 + 1.0 * 0.7, 1.0 * 0.3 + 0.0 * 0.7, 1.0 * 0.3 + 0.0 * 0.7, 0.3 * 0.3 + 1.0 * 0.7) =
(1.0, 0.3, 0.3, 0.79)。
因此,我们可以将67行的glBlendFunc(GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA);给注释掉,然后将63行的glColor4f(1.0f, 1.0f, 1.0f, 0.3f);改为:
glColor4f(1.0f, 0.3f, 0.3f, 0.79f);能够获得相同的效果。
我们上面讲述了如何用glBlendFunc函数来做颜色混合。但是这会带来一个问题,当我们进行了颜色混合后,最终目标颜色分量A同时也被改变了——原来A是0.3,混合后变成了0.79。
下面我们将引入另一个混合接口——glBlendFuncSeparate用于分别指定源和目标的颜色(RGB)分量和A(alpha)分量。下面给出该函数原型:
voidglBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
我们在制作游戏过程中会碰到这么种情况。比如有一层玻璃,然后后面有一些物体。
我们以前讨论过深度缓存问题,在光照这一讲中。深度缓存能够帮助我们把挡在前面物体之后的物体都裁剪调。那么对于同时存在不透明物体和透明物体的遮挡该如何处理呢?
我们首先要记住以下两种情况:
1、如果不透明物体在前,透明物体在后,那么不透明物体会把透明物体完全遮挡掉;
2、如果透明物体在前,不透明物体在后,那么透明物体与不透明物体做颜色混合。
此时,我们将通过开关混合、以及深度测试功能来完成这个任务。
下面先介绍下保持深度缓存的函数——
voidglDepthMask( GLboolean flag )flag的值为GL_TRUE和GL_FALSE。
// // MyView.m // OpenGLTest // // Created by Zenny Chen on 4/25/10. // Copyright 2010 GreenGames Studio. All rights reserved. // P44 #import "MyView.h" #include <OpenGL/OpenGL.h> #include <math.h> @implementation MyView - (id)initWithFrame:(NSRect)frame { self= [superinitWithFrame:frame]; if(self) { // Initialization code here. } returnself; } staticc*****t structVertexInfo { GLfloat vertices[3]; }vertexList[] = { // background rectangle {-0.8f, 0.8f, -2.0f}, {-0.8f, -0.8f, -2.0f}, {0.8f, 0.8f, -2.0f}, {0.8f, -0.8f, -2.0f}, // rectangle hidden {-0.5f, 0.5f, -1.0f}, {-0.5f, -0.5f, -1.0f}, {0.0f, 0.5f, -1.0f}, {0.0f, -0.5f, -1.0f}, // rectangle blended {0.0f, 0.5f, -1.0f}, {0.0f, -0.5f, -1.0f}, {0.5f, 0.5f, -1.0f}, {0.5f, -0.5f, -1.0f} }; - (void)prepareOpenGL { glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertexList); glFrontFace(GL_CCW); glCullFace(GL_BACK); glShadeModel(GL_SMOOTH); glClearColor(0.4, 0.4, 0.4, 1.0); glViewport(0, 0, 320, 320); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.0, 1.0, -1.0, 1.0, 1.0, 5.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBlendEquation(GL_FUNC_ADD); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } - (void)drawRect:(NSRect)dirtyRect { // Drawing code here. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_BLEND); glDepthMask(GL_TRUE); // Draw bakcgound rectangle glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // white glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Draw hidden rectangle glColor4f(1.0f, 0.0f, 0.0f, 0.3f); // red glDrawArrays(GL_TRIANGLE_STRIP, 4, 4); glEnable(GL_BLEND); glDepthMask(GL_FALSE); // Draw blended rectangle glColor4f(0.0f, 0.0f, 1.0f, 0.3f); // blue glDrawArrays(GL_TRIANGLE_STRIP, 8, 4); glFlush(); } @end取自: http://www.cocoachina.com/bbs/read.php?tid=26284&page=1