目 錄 15.1
融合 15.2
反走樣 15.3
霧
這一章主要講述OpenGL的三個特殊效果處理:融合、反走樣和霧。如果能很好地運用這些特殊效果,那么你會發現其中有無數的奧秘,你將用它們構造出千變萬化的圖形世界來。例如:透明的玻璃窗,半透明的紫色水晶,光滑的地板,五彩的肥皂泡,霧化的場景等等。同時,本章還簡單介紹一下有關的圖形學概念。
15.1、融合 15.1.1 Alpha值與融合(
Blending)
Alpha值在前面幾章中已經提到過,但是幾乎所有例程都將它設置為1.0,沒有詳細討論它為其它值時的情況。融合,是本章的重點,它是透明技術、數字合成和計算機繪畫技術的核心。固名思義,融合就是指兩種顏色各分量依據一定的比例混在一起合二為一。而這種比例就來源于Alpha值,即RGBA中的A或(r、g、b、a)中的a值,通常稱a為不透明性,稱(1-a)為透明性。因為在顏色表方式下不能說明a值,因此融合操作不能在顏色表方式下進行。
為了更好地理解這些概念,我們可以舉出一個例子來簡要說明。例如,坐在汽車內透過車窗的茶色玻璃看車外的綠樹,這些樹木的顏色并不是它們本來的綠色,而是透明的茶色與不透明的綠色的混合顏色。也就是說,最終的顏色來源于兩部分,一部分來自于玻璃的茶色,另一部分來自于樹木的綠色。兩部分所占的百分比依據玻璃的透射變化,若玻璃透射率為80%(即一束光照在其上有80%的透射過去),其不透明度為20%,即這里的a值就等于0.2,這樣進入眼中的樹木顏色是20%的玻璃顏色與80%的樹木本身顏色的合成。
15.1.2 融合因子(
Blending Factor)
在OpenGL的融合操作中,實際上包含了兩個因子的操作,這兩個因子就是源因子(
Source Factor)和目的因子(
Destination Factor)。從數學的角度來看,設源因子和目的因子分別為(Sr、Sg、Sb、Sa)與(Dr、Dg、Db、Da),則融合的最終結果是:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da) |
并且其中每個元素值都約簡到[0, 1]之間。在OpenGL中,由函數gjBlendFunc()來產生這兩個融合因子的值。其函數形式為:
void glBlendFunc(GLenum sfactor,GLenum dfactor)
控制源因子和目的因子的結合。參數sfactor指明怎樣計算源因子,dfactor指明怎樣計算目的因子。這些參數的可能值見表15-1所示。注意:融合因子的值在[0,1]范圍內,且兩因子結合后也要約簡到[0,1]內,源與目的的RGBA值分別帶有s和d下標。
常數 |
相關因子 |
計算后得到的融合因子 |
GL_ZERO |
源因子或目的因子 |
(0,0,0,0) |
GL_ONE |
源因子或目的因子 |
(1,1,1,1) |
GL_DST_COLOR |
源因子 |
(Rd,Gd,Bd,Ad) |
GL_SRC_COLOR |
目的因子 |
(Rs,Gs,Bs,As) |
GL_ONE_MINUS_DST_COLOR |
源因子 |
(1,1,1,1)-(Rd,Gd,Bd,Ad) |
GL_ONE_MINUS_SRC_COLOR |
目的因子 |
(1,1,1,1)-(Rs,Gs,Bs,As) |
GL_SRC_ALPHA |
源因子或目的因子 |
(As,As,As,As) |
GL_ONE_MINUS_SRC_ALPHA |
源因子或目的因子 |
(1,1,1,1)-(As,As,As,As) |
GL_DST_ALPHA |
源因子或目的因子 |
(Ad,Ad,Ad,Ad) |
GL_ONE_MINUS_DST_ALPHA |
源因子或目的因子 |
(1,1,1,1)-(Ad,Ad,Ad,Ad) |
GL_SRC_ALPHA_SATURATE |
源因子 |
(f,f,f,1); f=min(As,1-Ad) |
表15-1 源因子和目的因子 |
當用函數glBlendFunc()說明了融合因子的產生后,需調用函數glEnable(GL_BLEND)來啟動融合操作,不用時可調用glDisable(GL_BLEND)來關閉它。若源因子的參數為常數GL_ONE,目的因子的參數為常數GL_ZERO,則相當于關閉融合操作,這些值為缺省值。
15.1.3 融合實例
首先來看一個融合運用的簡單例子:
例15-1 Alpha二維融合例程(blend2d.c) #include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
/* 初始化 alpha 融合的參數 */
void myinit(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);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor4f (1.0, 0.0, 0.0, 0.7);
glRectf (0.25, 0.4, 0.75, 0.9);
glColor4f (0.0, 1.0, 0.0, 0.5);
glRectf (0.1, 0.1, 0.6, 0.6);
glColor4f (0.0, 0.0, 1.0, 0.3);
glRectf (0.4, 0.1, 0.9, 0.6);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, 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);
glMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Alpha 2D Blending");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是顯示三個不同顏色(紅、綠、藍)的方塊部分融合的效果。
 |
圖15-1 紅綠藍方塊融合效果 |
15.2、反走樣
反走樣(Antialiasing),又叫反混淆,是計算機圖形學中的一個重要概念。由于計算機生成的圖形是由離散點組成的數字化圖像,因而生成的圖形必然與真實景物之間存在一定誤差。這種誤差表現為圖形上的直線或光滑的曲線呈現鋸齒狀、彩色花紋失去原有的形態和色彩、細小物體在畫面上得不到反映等等。這種鋸齒就叫做走樣。見圖15-2所示,左邊為走樣線,右邊為反走樣線。
 |
圖15-2 走樣線與反走樣線 |
15.2.1 行為控制函數
在OpenGL中,許多細節的實現算法有所不同。這樣,可以調用函數glHint()對圖像質量和繪制速度之間的權衡作一些控制,但并非所有的實現都采用它。其函數形式為:
void glHint(GLenum target,GLenum hint);
控制OpenGL行為的某些方面。參數target說明控制什么行為,其可能值見表15-2所示。參數hint可以是:GL_FASTEST(即給出最有效的選擇)、GL_NICEST(即給出最高質量的選擇)、GL_DONT_CARE(即沒有選擇)。
參數 |
意義 |
GL_POINT_SMOOTH_HINT |
指定點、線、多邊形的采樣質量 |
GL_LINE_SMOOTH_HINT |
GL_POLYGON_SMOOTH_HINT |
GL_FOG_HINT |
指出霧的計算是按每個象素進行(GL_NICEST) 還是按每個頂點進行(GL_FASTEST) |
GL_PERSPECTIVE_CORRECTION_HINT |
指定顏色和紋理插值的質量 |
表15-2 函數glHint()參數及其意義 |
實際上,對于提示的解釋依賴于OpenGL的具體實現,有時可以忽略。參數GL_PERSPECTIVE_CORRECTION_HINT用于指定一個圖元中的顏色值和紋理值怎樣進行插值,要么在屏幕空間進行插值,要么按照透視投影糾正方式進行插值(這種方式需更多計算)。通常,系統對顏色進行線性插值,雖然從原理和技術的角度來講未必正確,但是人眼可以接受,且實現速度很快。然而紋理在大多數情況下,為了看起來可以接受,需要對其進行透視糾正插值。因此,可以用這個參數控制插值方法。同樣,在進行反走樣計算時,也要用到這個函數來控制圖形實現的行為。
15.2.2 點和線的反走樣
在OpenGL中雖然顏色表方式可以實現反走樣,但建議最好用RGBA方式進行。不管何種方式,對圖元進行反走樣,首先要用glEnable()啟動反走樣(參數為GL_POINT、GL_LINE_SMOOTH或GL_POLYGON_SMOOTH),同時,也可以調用glHint()提供一個圖像質量提示。但需注意的是,在RGBA方式下,必須啟動混合,最可能用的混合因子是GL_SRC_ALPHA(源)和GL_ONE_MINUS_SRC_ALPHA(目的)。另外,可以使目的因子為GL_ONE,則線的交點處要亮一些。下面舉出一個例子:
例15-2 反走樣線例程(antiline.c) #include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
/* 初始化反走樣為 RGBA 模式,同時包括alpha混合、提示、線寬等的設置。 */
void myinit(void)
{
glEnable (GL_LINE_SMOOTH);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glHint (GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glLineWidth (5.0);
glShadeModel(GL_FLAT);
glClearColor(0.0, 0.0, 0.0, 0.0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor4f (0.0, 0.6, 1.0, 1.0);
auxWireOctahedron(1.0);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 3.0, 5.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
glTranslatef (0.0, 0.0, -4.0); /* 將物體移至視見區內 */
glRotatef(15.0,1.0,1.0,0.0);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 400, 400);
auxInitWindow ("Antialiased Lines Using RGBA");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是顯示一個反走樣的網狀八面體。
 |
圖15-3 反走樣線 |
15.2.3 多邊形的反走樣
填充多邊形的反走樣類似于點線的反走樣,不同的只是將所有POINT或LINE的地方改為POLYGON而已。如下面的一個在RGBA模式下的多邊形反走樣例子:
例15-3 多邊形反走樣例程(antipoly.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
GLfloat mat_ambient[] = { 0.5, 0.5, 0.0, 1.00 };
GLfloat mat_diffuse[] = { 1.0, 0.8, 0.1, 1.0 };
GLfloat position[] = { 1.0, 0.0, 1.0, 0.0 };
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE,mat_diffuse);
glLightfv (GL_LIGHT0, GL_POSITION, position);
glEnable (GL_LIGHTING);
glEnable (GL_LIGHT0);
glEnable (GL_BLEND);
glCullFace (GL_BACK);
glEnable (GL_CULL_FACE);
glEnable (GL_POLYGON_SMOOTH);
glDisable (GL_DEPTH_TEST);
glBlendFunc (GL_SRC_ALPHA_SATURATE, GL_ONE);
glClearColor (0.0, 0.0, 0.0, 0.0);
}
void CALLBACK display(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glTranslatef (0.0, 0.0, -8.0);
glRotatef (-45.0, 1.0, 0.0, 0.0);
glRotatef (45.0, 0.0, 1.0, 0.0);
auxSolidIcosahedron (1.0);
glFlush ();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_ALPHA );
auxInitPosition (0, 0, 400, 400);
auxInitWindow ("Antialiased Polygons");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是顯示一個黃色的填充反走樣二十面體。
 |
圖15-4 反走樣多邊形 |
15.3、霧
15.3.1 霧的概論和例程
霧化效果在當今的計算機圖形學中應用極廣,它不僅可以使場景中的物體看起來更加真實,而且還可提高繪制速度。在很多情況下,計算機圖像有時會出現不符合實際的精細和棱角分明,上一節的反走樣技術可以通過光順著物體的邊界反走樣,使其看起來更真實,這一節的霧化處理可以使物體看起來更加自然,即在霧中,離視點遠的物體會變得模糊。
霧,是一個描述類似于大氣效果的一般術語,在視景仿真中應用很廣。它可以模擬煙霧(haze)、薄霧(mist)、濃煙(smoke)和污染(pollution)等效果。當啟動霧后,離視點較遠的物體開始淡化成霧的顏色,同時,霧的濃度可以控制,即隨著距離的增加物體變淡的速率可控,霧的顏色也可以任意設定。霧在兩種顏色方式下都能使用。下面舉出一個在RGBA方式下使用霧的例程:
例15-4 RGBA方式下霧化例程(fogrgb.c) #include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
#include <math.h>
#include <stdio.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
GLfloat mat_ambient[] = { 0.7, 0.6, 0.4, 1.00 };
GLfloat mat_diffuse[] = {0.7,0.0,0.99,1.0};
GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.00 };
GLfloat mat_shininess[] = { 15.0 };
GLfloat position[] = { 5.0, 5.0, 5.0, 1.0 };
GLfloat fogColor[4] = {0.6, 0.6, 0.6, 1.0};
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glLightfv(GL_LIGHT0, GL_POSITION, position);
glFrontFace (GL_CW);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_FOG);
{
glFogi (GL_FOG_MODE, GL_LINEAR);
glFogfv (GL_FOG_COLOR, fogColor);
glFogf (GL_FOG_START, 3.0);
glFogf (GL_FOG_END,15.0);
glHint (GL_FOG_HINT, GL_DONT_CARE);
glClearColor(0.3, 0.3, 0.3, 1.0);
}
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(-3.0,-1.5,-2.0);
auxSolidTorus(0.6,1.5);
glPopMatrix();
glPushMatrix();
glTranslatef(-0.5,-0.5,-7.0);
auxSolidTorus(0.6,1.5);
glPopMatrix();
glPushMatrix();
glTranslatef(2.0,0.8,-10.0);
auxSolidTorus(0.6,1.5);
glPopMatrix();
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= (h*3))
glOrtho (-6.0, 6.0, -2.0*((GLfloat) h*3)/(GLfloat) w, 2.0*((GLfloat) h*3)/(GLfloat) w, 0.0, 10.0);
else
glOrtho (-6.0*(GLfloat) w/((GLfloat) h*3), 6.0*(GLfloat) w/((GLfloat) h*3), -2.0, 2.0, 0.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 400);
auxInitWindow ("Fog_RGB_mode");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是顯示三個不同遠近的藍紫色環形圈在霧中的效果,這里的霧化計算采用線性方式(GL_LINEAR)。
 |
圖15-5 霧化效果 |
15.3.2 霧化步驟
在OpenGL程序中使用霧化效果非常容易。其步驟有三,如下:
1)啟動霧。函數調用為glEnable(GL_FOG);
2)控制霧。函數調用為glFog*(),用它選擇控制濃度和顏色的霧方程,其具體形式為: void glFog{if}[v](GLenum pname,TYPE param);
設置計算霧的參數和函數。若pname為GL_FOG_MODE,則param可以是GL_EXP(缺?。?、GL_EXP2或GL_LINEAR,分別代表三個霧因子。若pname為GL_FOG_DENSITY、GL_FOG_START或GL_FOG_END,則param為霧方程中對應的density、start和end的值。缺省值為1、0、1。在RGBA方式下,pname可以是GL_FOG_COLOR,此時參數param為指向含有RGBA值的向量指針。同樣,在顏色表方式下,pname相應值為GL_FOG_INDEX,其對應的param是霧的顏色索引值。
3)若有必要,可用函數glHint(GL_FOG_HINT)提供一個值。