深入了解OpenGL——颜色混合
大家好。這一講,我們講介紹OpenGL的顏色混合模式。
其實顏色混合用到的場合很多,比如多張圖片的合成,動畫游戲中的一些畫面特效等都可以通過顏色混合進行實現。最常用的混合方式就是實現物體與背景的半透明效果。另外,在制作2D游戲時顏色混合可以用來通過制作目標物體的蒙板實現移動。通過蒙板來消除舊位置的物體對象可以不必重繪當前整幀內容,而僅僅是發生變化的那些物體。為了各位從事iPhone開發的考慮。后面的代碼例子對OpenGL API的使用都會用OpenGL2.1與OpenGL ES1.1相互兼容的接口。
首先介紹一下OpenGL對源對象和目標對象進行顏色混合的實現。
這里,源對象是指你將要繪制的對象;目標對象是指已在幀緩存中的顏色。比如調用glClear(GL_COLOR_BUFFER_BIT);后留在幀緩存中的顏色。
在進行計算時,源和目標的混合都是在繪制源對象時進行計算的,在繪制對象以外的幀緩存像素不會受任何影響。
為了方便顏色混合,我們往往采用RGBA這種顏色模式。其中RGB表示色彩分量,而A就是混合因子(blend factor)。A,我們在圖形、圖像處理中常常表示為:alpha,它在圖像處理中常用作為透明系數。
我們指定了源和目標的混合因子后,OpenGL會對繪制對象的最終顏色做如下計算:
設:源對象的某個頂點的顏色為(Rs, Gs, Bs, As)
?????? 目的對象對應此源對象頂點的顏色為(Rd, Gd, Bd, Ad)
?????? 源混合因子為:(Sr, Sg, Sb, Sa)
??????目的混合因子為:(Dr, Dg, Db, Da)
那么,該頂點最終目標顏色為:
(Rs * Sr??<op>??Rd * Dr,??Gs * Sg??<op>??Gd * Dg,??Bs * Sb??<op>??Bd * Db,??As * Sa??<op>??Ad * Da)
其中,<op>可以是加法(+),減法(-), 逆向減法,最小值,最大值或按位邏輯操作,并且其優先級小于乘法(*)。
下面,我們介紹相關的OpenGL接口。
首先是開啟混合,使用glEnable(GL_BLEND);即可。
然后我們使用glBlendEquation()來指定混合操作,參數可以是:GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX。
但這里要注意的是,OpenGL ES1.1沒有glBlendEquation接口,因此只能做加法操作。
glBlendFunc()接口用于指定源混合因子與目標混合因子。
參數請參考紅寶石書中文版的第140頁,表6-1。
下面給出示例代碼:
// // 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 colorglColor4f(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)分量。下面給出該函數原型:
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
另外,這里要注意的是OpenGL ES1.1沒有glBlendFuncSeparate這個接口,而OpenGL ES2.0才開始支持這個接口。
我們在制作游戲過程中會碰到這么種情況。比如有一層玻璃,然后后面有一些物體。
我們以前討論過深度緩存問題,在光照這一講中。深度緩存能夠幫助我們把擋在前面物體之后的物體都裁剪調。那么對于同時存在不透明物體和透明物體的遮擋該如何處理呢?
我們首先要記住以下兩種情況:
1、如果不透明物體在前,透明物體在后,那么不透明物體會把透明物體完全遮擋掉;
2、如果透明物體在前,不透明物體在后,那么透明物體與不透明物體做顏色混合。
此時,我們將通過開關混合、以及深度測試功能來完成這個任務。
下面先介紹下保持深度緩存的函數——
voidglDepthMask( GLboolean flag ) flag的值為GL_TRUE和GL_FALSE。
如果用GL_TRUE來調用這個函數,那么深度緩存將會把后面繪制的頂點記錄到深度緩存內;如果用GL_FALSE來調用此函數,那么深度緩存將不會記錄后面繪制的頂點,而保持原來的記錄。
由此,我們的繪制順序為:
1、首先,關閉GL_BLEND功能以及打開glDepthMask,繪制所有不透明的物體;
2、打開GL_BLEND功能以及關閉glDepthMask,繪制所有透明物體。
這樣,前面的透明物體將不會完全遮擋后面的不透明物體。
下面是示例代碼。這個代碼首先繪制白色矩形,作為背景。然后繪制前景紅色矩形。該矩形是不透明的,因此會把后面白色背景完全遮蓋掉。
最后繪制一個前景藍色矩形。該矩形是透明的,因此將與白色背景做顏色混合。
// // 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 rectangleglColor4f(1.0f, 1.0f, 1.0f, 1.0f); // whiteglDrawArrays(GL_TRIANGLE_STRIP, 0, 4);// Draw hidden rectangleglColor4f(1.0f, 0.0f, 0.0f, 0.3f); // redglDrawArrays(GL_TRIANGLE_STRIP, 4, 4);glEnable(GL_BLEND);glDepthMask(GL_FALSE);// Draw blended rectangleglColor4f(0.0f, 0.0f, 1.0f, 0.3f); // blueglDrawArrays(GL_TRIANGLE_STRIP, 8, 4); glFlush(); } @end 取自: http://www.cocoachina.com/bbs/read.php?tid=26284&page=1
總結
以上是生活随笔為你收集整理的深入了解OpenGL——颜色混合的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 渗透测试笔试面试题目汇总
- 下一篇: JAVA反色计算方法的改进和修正