OpenGL中
的絕大多數特效都與某些類型的(色彩)混合有關。混色的定義為,將某個象素的顏色和已繪制在屏幕上與其對應的象素顏色相互結合。至于如何結合這兩個顏色則
依賴于顏色的alpha通道的分量值,以及/或者所使用的混色函數。Alpha通常是位于顏色值末尾的第4個顏色組成分量。前面這些課我們都是用
GL_RGB來指定顏色的三個分量。相應的GL_RGBA可以指定alpha分量的值。更進一步,我們可以使用glColor4f()來代替
glColor3f()。
絕大多數人都認為Alpha分量代表材料的透明度。這就是說,alpha值為0.0時所代表的材料是完全透明的。alpha值為1.0時所代表的材料則是完全不透明的。
8.1、混色的公式 若您對數學不感興趣,而只想看看如何實現透明,請跳過這一節。若您想深入理解(色彩)混合的工作原理,這一節應該適合您吧。(
譯者:
其實混合的基本原理是就將要分色的圖像各象素的顏色以及背景顏色均按照RGB規則各自分離之后,根據 —
圖像的RGB顏色分量*alpha值+背景的RGB顏色分量*(1-alpha值) —
這樣一個簡單公式來混合之后,最后將混合得到的RGB分量重新合并。)公式如下:
(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)
|
OpenGL按照上面的公式計算這兩個象素的混色結果。小寫的s和r分別代表源象素和目標象素。大寫的S和D則是相應的混色因子。這些決定了您如何對這些
象素混色。絕大多數情況下,各顏色通道的alpha混色值大小相同,這樣對源象素就有(As, As, As, As),目標象素則有(1, 1,
1, 1) - (As, As, As, As)。上面的公式就成了下面的模樣:
(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bs (1 - As), As As + Ad (1 - As))
|
這個公式會生成透明/半透明的效果。
8.2、OpenGL中的混色
在OpenGL中實現混色的步驟類似于我們以前提到的OpenGL過程。接著設置公式,并在繪制透明對象時關閉寫深度緩存。因為我們想在半透明的圖形背后繪制 對象。這不是正確的混色方法,但絕大多數時候這種做法在簡單的項目中都工作的很好。
Rui Martins的補充:
正確的混色過程應該是先繪制全部的場景之后再繪制透明的圖形。并且要按照與深度緩存相反的次序來繪制(先畫最遠的物體)。考慮對兩個多邊形(1和2)進行
alpha混合,不同的繪制次序會得到不同的結果。(這里假定多邊形1離觀察者最近,那么正確的過程應該先畫多邊形2,再畫多邊形1。正如您再現實中所見
到的那樣,從這兩個“透明的”多邊形背后照射來的光線總是先穿過多邊形2,再穿過多邊形1,最后才到達觀察者的眼睛)。
在深度緩存啟用時,您應該將透明圖形按照深度進行排序,并在全部場景繪制完畢之后再繪制這些透明物體。否則您將得到不正確的結果。我知道某些時候這樣做是
很令人痛苦的,但這是正確的方法。
我們將使用第七課的代碼。一開始先在代碼開始處增加兩個新的變量。出于清晰起見,我重寫了整段代碼。
#include <windows.h> // Windows的頭文件
#include <stdio.h> // 標準輸入/輸出庫的頭文件
#include <gl\gl.h> // OpenGL32庫的頭文件
#include <gl\glu.h> // GLu32庫的頭文件
#include <gl\glaux.h> // GLaux庫的頭文件
HGLRC hRC=NULL; // 永久著色描述表
HDC hDC=NULL; // 私有GDI設備描述表
HWND hWnd=NULL; // 保存我們的窗口句柄
HINSTANCE hInstance; // 保存程序的實例
bool keys[256]; // 用于鍵盤例程的數組
bool active=TRUE; // 窗口的活動標志,缺省為TRUE
bool fullscreen=TRUE; // 全屏標志缺省設定成全屏模式
BOOL light; // 光源的開/關
bool blend; // Blending 開/關 ( 新增 )
BOOL lp; // L鍵按下了么?
BOOL fp; // F鍵按下了么?
GLfloat xrot; // X 旋轉
GLfloat yrot; // Y 旋轉
GLfloat xspeed; // X 旋轉速度
GLfloat yspeed; // Y 旋轉速度
GLfloat z=-5.0f; // 深入屏幕的距離
GLfloat LightAmbient[]= { 0.5f }; // 環境光參數
GLfloat LightDiffuse[]= { 1.0f }; // 漫射光參數
GLfloat LightPosition[]= { 0.0f }; // 光源位置
GLuint filter; // 濾波類型
GLuint texture[3]; // 3種紋理的儲存空間
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // WndProc定義
然后往下移動到LoadGLTextures()這里。找到“if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))”這一行。我們現在使用有色玻璃紋理來代替上一課中的木箱紋理。
if (TextureImage[0]=LoadBMP("Data/glass.bmp")) // 載入玻璃位圖 (已修改)
在InitGL()代碼段加入以下兩行。第一行以全亮度繪制此物體,并對其進行50%的alpha混合(半透明)。當混合選項打開時,此物體將會產生50%的透明效果。第二行設置所采用的混合類型。Rui Martins的補充:alpha通道的值為0.0意味著物體材質是完全透明的。1.0則意味著完全不透明。
glColor4f(1.0f,1.0f,1.0f,0.5f); // 全亮度, 50% Alpha 混合(新增)
glBlendFunc(GL_SRC_ALPHA,GL_ONE); // 基于源象素alpha通道值的半透明混合函數 (新增)
在接近第七課結尾處的地方找到下面的代碼段。
if (keys[VK_LEFT]) // Left方向鍵按下了么?
{
yspeed-=0.01f; // 若是,減少yspeed
}
接著上面的代碼,我們增加如下的代碼。這幾行監視B鍵是否按下。如果是的話,計算機檢查混合選項是否已經打開。然后將其置為相反的狀態。
if (keys[VK_LEFT]) // Left方向鍵按下了么?
if (keys[’B’] && !bp) // B 健按下且bp為 FALSE么?
{
bp=TRUE; // 若是, bp 設為 TRUE
blend = !blend; // 切換混合選項的 TRUE / FALSE
if(blend) // 混合打開了么?
{
glEnable(GL_BLEND); // 打開混合
glDisable(GL_DEPTH_TEST); // 關閉深度測試
}
else // 否則
{
glDisable(GL_BLEND); // 關閉混合
glEnable(GL_DEPTH_TEST); // 打開深度測試
}
}
if (!keys[’B’]) // B 鍵松開了么?
{
bp=FALSE; // 若是, bp設為 FALSE
}
但是怎樣才能在使用紋理貼圖的時候指定混合時的顏色呢?很簡單,在調整貼圖模式時,文理貼圖的每個象素點的顏色都是由alpha通道參數與當前地象素顏
色相乘所得到的。比如,繪制的顏色是(0.5, 0.6, 0.4),我們會把顏色相乘得到(0.5, 0.6, 0.4,
0.2)(alpha參數在沒有指定時,缺省為1.0)。
就是如此。OpenGL實現Alpha混合的確很簡單。
原文注(11/13/1999)
我(NeHe)混色代碼進行了修改,以使顯示的物體看起來更逼真。同時對源象素和目的象素使用alpha參數來混合,會導致物體的人造痕跡看起來很明
顯。會使得物體的背面沿著側面的地方顯得更暗。基本上物體會看起來很怪異。我所用的混色方法也許不是最好的,但的確能夠工作。啟用光源之后,物體看起來很
逼真。感謝Tom提供的原始代碼,他采用的混色方法是正確的,但物體看起來并不象所期望的那樣吸引人。
代碼所作的再次修改是因為在某些顯卡上glDepthMask()函數存在尋址問題。這條命令在某些卡上啟用或關閉深度緩沖測試時似乎不是很有效,所以我已經將啟用或關閉深度緩沖測試的代碼轉成老式的glEnable和glDisable。
8.3、紋理貼圖的Alpha混合
用于紋理貼圖的alpha參數可以象顏色一樣從問題貼圖中讀取。方法如下,您需要在載入所需的材質同時取得其的alpha參數。然后在調用glTexImage2D()時使用GL_RGBA的顏色格式。