{
呵呵,這兩課早就翻譯好了,一直沒貼,大家久等了(有人再等嗎?)
簡(jiǎn)單的透明
OpenGL中的絕大多數(shù)特效都與某些類型的(色彩)混合有關(guān)。
混色的定義為,將某個(gè)象素的顏色和已繪制在屏幕上與其對(duì)應(yīng)的象素顏色相互結(jié)合。
至于如何結(jié)合這兩個(gè)顏色則依賴于顏色的alpha通道的分量值,以及/或者所使用的混色函數(shù)。
Alpha通常是位于顏色值末尾的第4個(gè)顏色組成分量。
前面這些課我們都是用GL_RGB來指定顏色的三個(gè)分量。
相應(yīng)的GL_RGBA可以指定alpha分量的值。
更進(jìn)一步,我們可以使用glColor4f()來代替glColor3f()。
絕大多數(shù)人都認(rèn)為Alpha分量代表材料的透明度。
這就是說,alpha值為0.0時(shí)所代表的材料是完全透明的。
alpha值為1.0時(shí)所代表的材料則是完全不透明的。
混色的公式
若您對(duì)數(shù)學(xué)不感冒,而只想看看如何實(shí)現(xiàn)透明,請(qǐng)?zhí)^這一節(jié)。
若您想深入理解(色彩)混合的工作原理,這一節(jié)應(yīng)該適合您吧。
『CKER的補(bǔ)充:其實(shí)并不難^-^。原文中的公式如下,CKER再嘮叨一下吧。
其實(shí)混合的基本原理是就將要分色的圖像各象素的顏色以及背景顏色均按照RGB規(guī)則各自分離之后,
根據(jù)-圖像的RGB顏色分量*alpha值+背景的RGB顏色分量*(1-alpha值)
-這樣一個(gè)簡(jiǎn)單公式來混合之后,最后將混合得到的RGB分量重新合并。』
公式如下:
(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)
OpenGL按照上面的公式計(jì)算這兩個(gè)象素的混色結(jié)果。
小寫的s和r分別代表源象素和目標(biāo)象素。大寫的S和D則是相應(yīng)的混色因子。
這些決定了您如何對(duì)這些象素混色。
絕大多數(shù)情況下,各顏色通道的alpha混色值大小相同,
這樣對(duì)源象素就有 (As, As, As, As),
目標(biāo)象素則有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))
這個(gè)公式會(huì)生成透明/半透明的效果。
OpenGL中的混色
在OpenGL中實(shí)現(xiàn)混色的步驟類似于我們以前提到的OpenGL過程。
接著設(shè)置公式,并在繪制透明對(duì)象時(shí)關(guān)閉寫深度緩存。
因?yàn)槲覀兿朐诎胪该鞯膱D形背后繪制 對(duì)象。
這不是正確的混色方法,但絕大多數(shù)時(shí)候這種做法在簡(jiǎn)單的項(xiàng)目中都工作的很好。
Rui Martins 的補(bǔ)充: 正確的混色過程應(yīng)該是先繪制全部的場(chǎng)景之后再繪制透明的圖形。
并且要按照與深度緩存相反的次序來繪制(先畫最遠(yuǎn)的物體)。
考慮對(duì)兩個(gè)多邊形(1和2)進(jìn)行alpha混合,不同的繪制次序會(huì)得到不同的結(jié)果。
(這里假定多邊形1離觀察者最近,那么正確的過程應(yīng)該先畫多邊形2,再畫多邊形1。
正如您再現(xiàn)實(shí)中所見到的那樣,
從這兩個(gè)<透明的>多邊形背后照射來的光線總是先穿過多邊形2,
再穿過多邊形1,最后才到達(dá)觀察者的眼睛。)
在深度緩存啟用時(shí),您應(yīng)該將透明圖形按照深度進(jìn)行排序,
并在全部場(chǎng)景繪制完畢之后再繪制這些透明物體。否則您將得到不正確的結(jié)果。
我知道某些時(shí)候這樣做是很令人痛苦的,但這是正確的方法。
我們將使用第七課的代碼。
一開始先在代碼開始處增加兩個(gè)新的變量。出于清晰起見,我重寫了整段代碼。
}
Var
h_RC : HGLRC; // Rendering Context(著色描述表)。
h_DC : HDC; // Device Context(設(shè)備描述表)
h_Wnd : HWND; // 窗口句柄
h_Instance : HINST; // 程序Instance(實(shí)例)。
keys : Array[0..255] Of Boolean; // 用于鍵盤例程的數(shù)組
light : Boolean; // 光源的開/關(guān)
blend : Boolean; // Blending OFF/ON? ( 新增 )
lp : Boolean; // L鍵按下了么?
fp : Boolean; // F鍵按下了么?
bp : Boolean; // B 鍵按下了么? ( 新增 )
xrot : GLfloat; // X 旋轉(zhuǎn)
yrot : GLfloat; // Y 旋轉(zhuǎn)
xspeed : GLfloat; // X 旋轉(zhuǎn)速度
yspeed : GLfloat; // Y 旋轉(zhuǎn)速度
z : GLfloat = -5.0 f; // 深入屏幕的距離
LightAmbient : Array[0..3] Of GLfloat = (0.5, 0.5, 0.5, 1.0); //環(huán)境光參數(shù) ( 新增 )
LightDiffuse : Array[0..3] Of GLfloat = (1.0, 1.0, 1.0, 1.0); // 漫射光參數(shù) ( 新增 )
LightPosition : Array[0..3] Of GLfloat = (0.0, 0.0, 2.0, 1.0); // 光源位置 ( 新增 )
filter : GLuint; // 濾波類型
texture : Array[0..2] Of GLuint; // 3種紋理的儲(chǔ)存空間
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';
{
然后往下移動(dòng)到 LoadGLTextures() 這里。
找到 if (TextureImage[0]=LoadBMP('Data/Crate.bmp'))
這一行。我們現(xiàn)在使用有色玻璃紋理來代替上一課中的木箱紋理。
if (TextureImage[0]=LoadBMP("Data/glass.bmp")); // 載入玻璃位圖 ( 已修改 )
}
Function LoadTexture: boolean; // 載入位圖并轉(zhuǎn)換成紋理
Var
Status : boolean; // Status 指示器
TextureImage : Array[0..1] Of PTAUX_RGBImageRec; // 創(chuàng)建紋理的存儲(chǔ)空間
Begin
Status := false;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // 將指針設(shè)為 NULL
TextureImage[0] := LoadBMP('Walls.bmp');
If TextureImage[0] <> Nil Then
Begin
Status := TRUE; // 將 Status 設(shè)為 TRUE
glGenTextures(1, texture[0]); // 創(chuàng)建紋理
// 創(chuàng)建 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]); // 使用來自位圖數(shù)據(jù)生成 的典型紋理
// 生成紋理
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); // 線形濾波
// 創(chuàng)建 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; // 釋放紋理圖像占用的內(nèi)存
TextureImage[0] := Nil; // 釋放圖像結(jié)構(gòu)
result := Status; // 返回 Status
End;
{
在glInit()代碼段加入以下兩行。
第一行以全亮度繪制此物體,并對(duì)其進(jìn)行50%的alpha混合(半透明)。
當(dāng)混合選項(xiàng)打開時(shí),此物體將會(huì)產(chǎn)生50%的透明效果。
第二行設(shè)置所采用的混合類型。
Rui Martins 的補(bǔ)充:
alpha通道的值為 0.0意味著物體材質(zhì)是完全透明的。
1.0 則意味著完全不透明。
}
Procedure glInit(); // 此處開始對(duì)OpenGL進(jìn)行所有設(shè)置
Begin
If (Not LoadTexture) Then // 調(diào)用紋理載入子例程
exit; // 如果未能載入,退出
glEnable(GL_TEXTURE_2D); // 啟用紋理映射
glShadeModel(GL_SMOOTH); // 啟用陰影平滑
glClearColor(0.0, 0.0, 0.0, 0.0); // 黑色背景
glClearDepth(1.0); // 設(shè)置深度緩存
glEnable(GL_DEPTH_TEST); // 啟用深度測(cè)試
glDepthFunc(GL_LESS); // 所作深度測(cè)試的類型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //高度優(yōu)化的透視投影計(jì)算
glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient[0]); // 設(shè)置環(huán)境光
glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse[0]); // 設(shè)置漫射光
glLightfv(GL_LIGHT1, GL_POSITION, @LightPosition); // 光源位置
glEnable(GL_LIGHT1); // 啟用一號(hào)光源
glColor4f(1.0, 1.0, 1.0, 0.5); // 全亮度, 50% Alpha 混合( 新增 )
glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基于源象素alpha通道值的半透明混合函數(shù) ( 新增 )
End;
{在接近第七課結(jié)尾處的地方找到下面的代碼段。
If keys[VK_LEFT] Then //Left方向鍵按下了么?
yspeed := yspeed - 0.01; //若是, 減少yspeed
接著上面的代碼,我們?cè)黾尤缦碌拇a。
這幾行監(jiān)視B鍵是否按下。
如果是的話,計(jì)算機(jī)檢查混合選項(xiàng)是否已經(jīng)打開。
然后將其置為相反的狀態(tài)。
}
If (keys[ord('B')] And Not bp) Then //B 健按下且bp為 FALSE么?
Begin
bp := TRUE; // 若是, bp 設(shè)為 TRUE
blend := Not blend; // 切換混合選項(xiàng)的 TRUE / FALSE
If (blend) Then // 混合打開了么?
Begin
glEnable(GL_BLEND); // 打開混合
glDisable(GL_DEPTH_TEST); // 關(guān)閉深度測(cè)試
End
Else // 否則
Begin
glDisable(GL_BLEND); // 關(guān)閉混合
glEnable(GL_DEPTH_TEST); // 打開深度測(cè)試
End;
End;
If (Not keys[ord('B')]) Then // B 鍵松開了么?
Begin
bp := FALSE; // 若是, bp設(shè)為 FALSE
End;
{
但是怎樣才能在使用紋理貼圖的時(shí)候指定混合時(shí)的顏色呢?很簡(jiǎn)單,
在調(diào)整貼圖模式時(shí),文理貼圖的每個(gè)象素點(diǎn)的顏色都是由alpha通道參數(shù)
與當(dāng)前地象素顏色相乘所得到的。
比如,繪制的顏色是 (0.5, 0.6, 0.4),
我們會(huì)把顏色相乘得到(0.5, 0.6, 0.4, 0.2)
(alpha參數(shù)在沒有指定時(shí),缺省為零)。
就是如此!OpenGL實(shí)現(xiàn)Alpha混合的確很簡(jiǎn)單!
}
{
原文注 (11/13/99)
我(NeHe)混色代碼進(jìn)行了修改,以使顯示的物體看起來更逼真。
同時(shí)對(duì)源象素和目的象素使用alpha參數(shù)來混合,會(huì)導(dǎo)致物體的人造痕跡看起來很明顯。
會(huì)使得物體的背面沿著側(cè)面的地方顯得更暗。
基本上物體會(huì)看起來很怪異。
我所用的混色方法也許不是最好的,但的確能夠工作。
啟用光源之后,物體看起來很逼真。
感謝Tom提供的原始代碼,他采用的混色方法是正確的,
但物體看起來并不象所期望的那樣吸引人:)
代碼所作的再次修改是因?yàn)樵谀承╋@卡上glDepthMask()函數(shù)存在尋址問題。
這條命令在某些卡上啟用或關(guān)閉深度緩沖測(cè)試時(shí)似乎不是很有效,
所以我已經(jīng)將啟用或關(guān)閉深度緩沖測(cè)試的代碼轉(zhuǎn)成老式的glEnable和glDisable。
紋理貼圖的Alpha混合
用于紋理貼圖的alpha參數(shù)可以象顏色一樣從問題貼圖中讀取。
方法如下,您需要在載入所需的材質(zhì)同時(shí)取得其的alpha參數(shù)。
然后在調(diào)用glTexImage2D()時(shí)使用GL_RGBA的顏色格式。
}
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注