{
呵呵,這兩課早就翻譯好了,一直沒貼,大家久等了(有人再等嗎?)
簡單的透明
OpenGL中的絕大多數特效都與某些類型的(色彩)混合有關。
混色的定義為,將某個像素的顏色和已繪製在螢幕上與其對應的像素顏色相互結合。
至於如何結合這兩個顏色則依賴於顏色的alpha通道的分量值,以及/或所使用的混色函數。
Alpha通常是位於顏色值末端的第4個顏色組成分量。
前面這些課我們都是用GL_RGB來指定顏色的三個分量。
對應的GL_RGBA可以指定alpha分量的值。
更進一步,我們可以使用glColor4f()來取代glColor3f()。
絕大多數人都認為Alpha分量代表材料的透明度。
這就是說,alpha值為0.0時所代表的材料是完全透明的。
alpha值為1.0時所代表的材料則是完全不透明的。
混色的公式
若您對數學不感冒,只想看看如何透明,請跳過這一節。
若您想深入理解(色彩)混合的工作原理,這一節應該適合您。
『CKER的補充:其實不難^-^。原文中的公式如下,CKER再嘮叨一下吧。
其實混合的基本原理是就將要分色的影像各像素的顏色以及背景顏色都依照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))
這個公式會產生透明/半透明的效果。
OpenGL中的混色
在OpenGL中實現混色的步驟類似於我們先前提到的OpenGL過程。
接著設定公式,並在繪製透明物件時關閉寫入深度快取。
因為我們想在半透明的圖形背後繪製物件。
這不是正確的混色方法,但絕大多數時候這種做法在簡單的專案中都運作的很好。
Rui Martins 的補充: 正確的混色過程應該是先繪製全部的場景之後再繪製透明的圖形。
並且要按照與深度緩存相反的次序來繪製(先畫最遠的物體)。
考慮將兩個多邊形(1和2)進行alpha混合,不同的繪製次序會得到不同的結果。
(這裡假定多邊形1離觀察者最近,那麼正確的過程應該先畫多邊形2,再畫多邊形1。
正如您再現實中所見到的那樣,
從這兩個<透明的>多邊形背後照射來的光線總是先穿過多邊形2,
再穿過多邊形1,最後才到達觀察者的眼睛。 )
在深度快取啟用時,您應該將透明圖形按照深度進行排序,
並在全部場景繪製完畢之後再繪製這些透明物件。否則您將得到不正確的結果。
我知道某些時候這樣做是很痛苦的,但這是正確的方法。
我們將使用第七課的代碼。
一開始先在程式碼開始處增加兩個新的變數。出於清晰起見,我重寫了整段程式碼。
}
Var
h_RC : HGLRC; // Rendering Context(著色描述表)。
h_DC : HDC; // Device Context(裝置描述表)
h_Wnd : HWND; // 視窗句柄
h_Instance : HINST; // 程式Instance(實例)。
keys : Array[0..255] Of Boolean; // 用於鍵盤例程的陣列
light : Boolean; // 光源的開/關
blend : 布林; // Blending OFF/ON? ( 新增)
lp : Boolean; // L鍵按下了麼?
fp : Boolean; // F鍵按下了麼?
bp : Boolean; // B 鍵按下了麼? ( 新增)
xrot : GLfloat; // X 旋轉
yrot : GLfloat; // Y 旋轉
xspeed : GLfloat; // X 旋轉速度
yspeed : GLfloat; // Y 旋轉速度
z : GLfloat = -5.0 f; // 深入螢幕的距離
LightAmbient : Array[0..3] Of GLfloat = (0.5, 0.5, 0.5, 1.0); //環境光參數( 新增)
LightDiffuse : Array[0..3] Of GLfloat = (1.0, 1.0, 1.0, 1.0); // 漫射光參數( 新增)
LightPosition : Array[0..3] Of GLfloat = (0.0, 0.0, 2.0, 1.0); // 光源位置( 新增)
filter : GLuint; // 濾波型
texture : Array[0..2] Of GLuint; // 3種紋理的儲存空間
PRocedure glGenTextures(n: GLsizei; Var textures: GLuint); stdcall; external
opengl32;
Procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external
opengl32;
Function gluBuild2DMipmaps(target: GLenum; components, width, height: GLint;
format, atype: GLenum; data: Pointer): Integer; stdcall; external glu32 name
'gluBuild2DMipmaps';
{
然後往下移動到LoadGLTextures() 這裡。
找到if (TextureImage[0]=LoadBMP('Data/Crate.bmp'))
這一行。我們現在使用有色玻璃紋理來代替上一課中的木箱紋理。
if (TextureImage[0]=LoadBMP("Data/glass.bmp")); // 載入玻璃位圖( 已修正)
}
Function LoadTexture: boolean; // 載入點陣圖並轉換成紋理
Var
Status : boolean; // Status 指示器
TextureImage : Array[0..1] Of PTAUX_RGBImageRec; // 建立紋理的儲存空間
Begin
Status := false;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // 將指標設為NULL
TextureImage[0] := LoadBMP('Walls.bmp');
If TextureImage[0] <> Nil Then
Begin
Status := TRUE; // 將Status 設為TRUE
glGenTextures(1, texture[0]); // 建立紋理
// 建立Nearest 濾波貼圖
glBindTexture(GL_TEXTURE_2D, texture[0]);
// 產生紋理
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // ( 新增)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // ( 新增)
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data);
glBindTexture(GL_TEXTURE_2D, texture[1]); // 使用來自點陣圖資料產生的典型紋理
// 產生紋理
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 線形濾波
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 線形濾波
// 建立MipMapped 紋理
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST); // ( 新增)
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0].sizeX,
TextureImage[0].sizey, GL_RGB, GL_UNSIGNED_BYTE,
TextureImage[0].data); //(新增) }
End;
If assigned(TextureImage[0]) Then // 紋理是否存在
If assigned(TextureImage[0].data) Then // 紋理影像是否存在
TextureImage[0].data := Nil; // 釋放紋理影像所佔用的內存
TextureImage[0] := Nil; // 釋放圖像結構
result := Status; // 回傳Status
End;
{
在glInit()程式碼段加入以下兩行。
第一行以全亮度繪製此物體,並對其進行50%的alpha混合(半透明)。
當混合選項開啟時,此物體將會產生50%的透明效果。
第二行設定所採用的混合類型。
Rui Martins 的補充:
alpha通道的值為0.0表示物體材質是完全透明的。
1.0 則意味著完全不透明。
}
Procedure glInit(); // 此處開始對OpenGL進行所有設置
Begin
If (Not LoadTexture) Then // 呼叫紋理載入子例程
exit; // 若未能載入,退出
glEnable(GL_TEXTURE_2D); // 啟用紋理映射
glShadeModel(GL_SMOOTH); // 啟用陰影平滑
glClearColor(0.0, 0.0, 0.0, 0.0); // 黑色背景
glClearDepth(1.0); // 設定深度緩存
glEnable(GL_DEPTH_TEST); // 啟用深度測試
glDepthFunc(GL_LESS); // 所做深度測試的型別
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //高度最佳化的透視投影計算
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient[0]); // 設定環境光
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse[0]); // 設定漫射光
glLightfv(GL_LIGHT1, GL_POSITION, @LightPosition); // 光源位置
glEnable(GL_LIGHT1); // 啟用一號光源
glColor4f(1.0, 1.0, 1.0, 0.5); // 全亮度, 50% Alpha 混合( 新增)
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基於源像素alpha通道值的半透明混合函數( 新增)
End;
{在接近第七課結尾處的地方找到下面的代碼段。
If keys[VK_LEFT] Then //Left方向鍵按下了麼?
yspeed := yspeed - 0.01; //若是, 減少yspeed
接著上面的程式碼,我們增加如下的程式碼。
這幾行監視B鍵是否按下。
如果是的話,計算機檢查混合選項是否已經打開。
然後將其置為相反的狀態。
}
If (keys[ord('B')] And Not bp) Then //B 健按下且bp為FALSE麼?
Begin
bp := TRUE; // 若是, bp 設為TRUE
blend := Not blend; // 切換混合選項的TRUE / FALSE
If (blend) Then // 混合打開了麼?
Begin
glEnable(GL_BLEND); // 開啟混合
glDisable(GL_DEPTH_TEST); // 關閉深度測試
End
Else // 否則
Begin
glDisable(GL_BLEND); // 關閉混合
glEnable(GL_DEPTH_TEST); // 開啟深度測試
End;
End;
If (Not keys[ord('B')]) Then // B 鍵鬆開了什麼?
Begin
bp := FALSE; // 若是, bp設為FALSE
End;
{
但是怎麼才能在使用紋理貼圖的時候指定混合時的顏色呢?很簡單,
在調整貼圖模式時,文理貼圖的每個像素點的顏色都是由alpha通道參數
與目前地像素顏色相乘所得到的。
例如,繪製的顏色是(0.5, 0.6, 0.4),
我們會把顏色相乘得到(0.5, 0.6, 0.4, 0.2)
(alpha參數在沒有指定時,預設為零)。
就是如此! OpenGL實作Alpha混合的確很簡單!
}
{
原文註(11/13/99)
我(NeHe)混色程式碼進行了修改,以使顯示的物體看起來更逼真。
同時對源像素和目的像素使用alpha參數來混合,會導致物體的人造痕跡看起來很明顯。
會使得物體的背面沿著側面的地方顯得更暗。
基本上物體會看起來很怪。
我所用的混色方法也許不是最好的,但的確能夠運作。
啟用光源之後,物體看起來很逼真。
感謝Tom提供的原始程式碼,他採用的混色方法是正確的,
但物體看起來並不像所期望的那樣吸引人:)
程式碼所作的再次修改是因為在某些顯示卡上glDepthMask()函數有尋址問題。
這條命令在某些卡上啟用或關閉深度緩衝測試時似乎不是很有效,
所以我已經將啟用或關閉深度緩衝測試的程式碼轉換成老式的glEnable和glDisable。
紋理貼圖的Alpha混合
用於紋理貼圖的alpha參數可以像顏色一樣從問題貼圖中讀取。
方法如下,您需要在載入所需的材質中同時取得其的alpha參數。
然後在呼叫glTexImage2D()時使用GL_RGBA的顏色格式。
}