OpenGL中混色的用法

混合是一種常用的技巧,通常可以用來實現半透明。但其實它也是十分靈活的,你可以通過不同的設置得到不同的混合結果,產生一些有趣或者奇怪的圖象。

混合是什麼呢?混合就是把兩種顏色混在一起。具體一點,就是把某一像素位置原來的顏色和將要畫上去的顏色,通過某種方式混在一起,從而實現特殊的效果。
假設我們需要繪製這樣一個場景:透過紅色的玻璃去看綠色的物體,那麼可以先繪製綠色的物體,再繪製紅色玻璃。在繪製紅色玻璃的時候,利用“混合”功能,把將要繪製上去的紅色和原來的綠色進行混合,於是得到一種新的顏色,看上去就好像玻璃是半透明的。
要使用OpenGL的混合功能,只需要調用:glEnable(GL_BLEND);即可。
要關閉OpenGL的混合功能,只需要調用:glDisable(GL_BLEND);即可。
注意:只有在RGBA模式下,纔可以使用混合功能,顏色索引模式下是無法使用混合功能的。

一、源因子和目標因子
前面我們已經提到,混合需要把原來的顏色和將要畫上去的顏色找出來,經過某種方式處理後得到一種新的顏色。這裏把將要畫上去的顏色稱爲“源顏色”,把原來的顏色稱爲“目標顏色”。
OpenGL 會把源顏色和目標顏色各自取出,並乘以一個係數(源顏色乘以的係數稱爲“源因子”,目標顏色乘以的係數稱爲“目標因子”),然後相加,這樣就得到了新的顏 色。(也可以不是相加,新版本的OpenGL可以設置運算方式,包括加、減、取兩者中較大的、取兩者中較小的、邏輯運算等,但我們這裏爲了簡單起見,不討 論這個了)
下面用數學公式來表達一下這個運算方式。假設源顏色的四個分量(指紅色,綠色,藍色,alpha值)是 (Rs, Gs, Bs, As),目標顏色的四個分量是(Rd, Gd, Bd, Ad),又設源因子爲(Sr, Sg, Sb, Sa),目標因子爲 (Dr, Dg, Db, Da)。則混合產生的新顏色可以表示爲:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)
當然了,如果顏色的某一分量超過了1.0,則它會被自動截取爲1.0,不需要考慮越界的問題。

源因子和目標因子是可以通過glBlendFunc函數來進行設置的。glBlendFunc有兩個參數,前者表示源因子,後者表示目標因子。這兩個參數可以是多種值,下面介紹比較常用的幾種。
GL_ZERO:     表示使用0.0作爲因子,實際上相當於不使用這種顏色參與混合運算。
GL_ONE:      表示使用1.0作爲因子,實際上相當於完全的使用了這種顏色參與混合運算。
GL_SRC_ALPHA:表示使用源顏色的alpha值來作爲因子。
GL_DST_ALPHA:表示使用目標顏色的alpha值來作爲因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0減去源顏色的alpha值來作爲因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0減去目標顏色的alpha值來作爲因子。
除 此以外,還有GL_SRC_COLOR(把源顏色的四個分量分別作爲因子的四個分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前兩個在OpenGL舊版本中只能用於設置目標因子,後兩個在OpenGL 舊版本中只能用於設置源因子。新版本的OpenGL則沒有這個限制,並且支持新的GL_CONST_COLOR(設定一種常數顏色,將其四個分量分別作爲 因子的四個分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、 GL_ONE_MINUS_CONST_ALPHA。另外還有GL_SRC_ALPHA_SATURATE。新版本的OpenGL還允許顏色的alpha 值和RGB值採用不同的混合因子。但這些都不是我們現在所需要了解的。畢竟這還是入門教材,不需要整得太複雜~

舉例來說:
如果設置了glBlendFunc(GL_ONE, GL_ZERO);,則表示完全使用源顏色,完全不使用目標顏色,因此畫面效果和不使用混合的時候一致(當然效率可能會低一點點)。如果沒有設置源因子和目標因子,則默認情況就是這樣的設置。
如果設置了glBlendFunc(GL_ZERO, GL_ONE);,則表示完全不使用源顏色,因此無論你想畫什麼,最後都不會被畫上去了。(但這並不是說這樣設置就沒有用,有些時候可能有特殊用途)
如 果設置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,則表示源顏色乘以自身的alpha 值,目標顏色乘以1.0減去源顏色的alpha值,這樣一來,源顏色的alpha值越大,則產生的新顏色中源顏色所佔比例就越大,而目標顏色所佔比例則減 小。這種情況下,我們可以簡單的將源顏色的alpha值理解爲“不透明度”。這也是混合時最常用的方式。
如果設置了glBlendFunc(GL_ONE, GL_ONE);,則表示完全使用源顏色和目標顏色,最終的顏色實際上就是兩種顏色的簡單相加。例如紅色(1, 0, 0)和綠色(0, 1, 0)相加得到(1, 1, 0),結果爲黃色。
注意:
所 謂源顏色和目標顏色,是跟繪製的順序有關的。假如先繪製了一個紅色的物體,再在其上繪製綠色的物體。則綠色是源顏色,紅色是目標顏色。如果順序反過來,則 紅色就是源顏色,綠色纔是目標顏色。在繪製時,應該注意順序,使得繪製的源顏色與設置的源因子對應,目標顏色與設置的目標因子對應。不要被混亂的順序搞暈 了。

二、二維圖形混合舉例
下面看一個簡單的例子,實現將兩種不同的顏色混合在一起。爲了便於觀察,我們繪製兩個矩形:glRectf(-1, -1, 0.5, 0.5);glRectf(-0.5, -0.5, 1, 1);,這兩個矩形有一個重疊的區域,便於我們觀察混合的效果。
先來看看使用glBlendFunc(GL_ONE, GL_ZERO);的,它的結果與不使用混合時相同。
void myDisplay(void)
{
glClear(GL_COLOR_BUFFER_BIT);

glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ZERO);

glColor4f(1, 0, 0, 0.5);
glRectf(-1, -1, 0.5, 0.5);
glColor4f(0, 1, 0, 0.5);
glRectf(-0.5, -0.5, 1, 1);

glutSwapBuffers();
}
嘗 試把glBlendFunc的參數修改爲glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);以及 glBlendFunc(GL_ONE, GL_ONE);,觀察效果。第一種情況下,效果與沒有使用混合時相同,後繪製的圖形會覆蓋先繪製的圖形。第二 種情況下,alpha被當作“不透明度”,由於被設置爲0.5,所以兩個矩形看上去都是半透明的,乃至於看到黑色背景。第三種是將顏色相加,紅色和綠色相 加得到黃色。


三、實現三維混合
也許你迫不及待的想要繪製一個三維的帶有半透明物體的場景了。但是現在恐怕還不行,還有一點是在進行三維場景的混合時必須注意的,那就是深度緩衝。
深 度緩衝是這樣一段數據,它記錄了每一個像素距離觀察者有多近。在啓用深度緩衝測試的情況下,如果將要繪製的像素比原來的像素更近,則像素將被繪製。否則, 像素就會被忽略掉,不進行繪製。這在繪製不透明的物體時非常有用——不管是先繪製近的物體再繪製遠的物體,還是先繪製遠的物體再繪製近的物體,或者乾脆以 混亂的順序進行繪製,最後的顯示結果總是近的物體遮住遠的物體。
然而在你需要實現半透明效果時,發現一切都不是那麼美好了。如果你繪製了一個近距離的半透明物體,則它在深度緩衝區內保留了一些信息,使得遠處的物體將無法再被繪製出來。雖然半透明的物體仍然半透明,但透過它看到的卻不是正確的內容了。
要 解決以上問題,需要在繪製半透明物體時將深度緩衝區設置爲只讀,這樣一來,雖然半透明物體被繪製上去了,深度緩衝區還保持在原來的狀態。如果再有一個物體 出現在半透明物體之後,在不透明物體之前,則它也可以被繪製(因爲此時深度緩衝區中記錄的是那個不透明物體的深度)。以後再要繪製不透明物體時,只需要再 將深度緩衝區設置爲可讀可寫的形式即可。嗯?你問我怎麼繪製一個一部分半透明一部分不透明的物體?這個好辦,只需要把物體分爲兩個部分,一部分全是半透明 的,一部分全是不透明的,分別繪製就可以了。
即使使用了以上技巧,我們仍然不能隨心所欲的按照混亂順序來進行繪製。必須是先繪製不透明的物體,然 後繪製透明的物體。否則,假設背景爲藍色,近處一塊紅色玻璃,中間一個綠色物體。如果先繪製紅色半透明玻璃的話,它先和藍色背景進行混合,則以後繪製中間 的綠色物體時,想單獨與紅色玻璃混合已經不能實現了。
總結起來,繪製順序就是:首先繪製所有不透明的物體。如果兩個物體都是不透明的,則誰先誰後 都沒有關係。然後,將深度緩衝區設置爲只讀。接下來,繪製所有半透明的物體。如果兩個物體都是半透明的,則誰先誰後只需要根據自己的意願(注意了,先繪製 的將成爲“目標顏色”,後繪製的將成爲“源顏色”,所以繪製的順序將會對結果造成一些影響)。最後,將深度緩衝區設置爲可讀可寫形式。
調用glDepthMask(GL_FALSE);可將深度緩衝區設置爲只讀形式。調用glDepthMask(GL_TRUE);可將深度緩衝區設置爲可讀可寫形式。
一 些網上的教程,包括大名鼎鼎的NeHe教程,都在使用三維混合時直接將深度緩衝區禁用,即調用glDisable(GL_DEPTH_TEST);。這樣 做並不正確。如果先繪製一個不透明的物體,再在其背後繪製半透明物體,本來後面的半透明物體將不會被顯示(被不透明的物體遮住了),但如果禁用深度緩衝, 則它仍然將會顯示,並進行混合。NeHe提到某些顯卡在使用glDepthMask函數時可能存在一些問題,但可能是由於我的閱歷有限,並沒有發現這樣的 情況。

那麼,實際的演示一下吧。我們來繪製一些半透明和不透明的球體。假設有三個球體,一個紅色不透明的,一個綠色半透明的,一個藍色半透明的。紅色最遠,綠色 在中間,藍色最近。根據前面所講述的內容,紅色不透明球體必須首先繪製,而綠色和藍色則可以隨意修改順序。這裏爲了演示不注意設置深度緩衝的危害,我們故 意先繪製最近的藍色球體,再繪製綠色球體。
爲了讓這些球體有一點立體感,我們使用光照。在(1, 1, -1)處設置一個白色的光源。代碼如下:
void setLight(void)
{
static const GLfloat light_position[] = {1.0f, 1.0f, -1.0f, 1.0f};
static const GLfloat light_ambient[]  = {0.2f, 0.2f, 0.2f, 1.0f};
static const GLfloat light_diffuse[]  = {1.0f, 1.0f, 1.0f, 1.0f};
static const GLfloat light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};

glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
glEnable(GL_DEPTH_TEST);
}
每一個球體顏色不同。所以它們的材質也都不同。這裏用一個函數來設置材質。
void setMatirial(const GLfloat mat_diffuse[4], GLfloat mat_shininess)
{
static const GLfloat mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f};
static const GLfloat mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
glMaterialfv(GL_FRONT, GL_EMISSION,  mat_emission);
glMaterialf (GL_FRONT, GL_SHININESS, mat_shininess);
}
有了這兩個函數,我們就可以根據前面的知識寫出整個程序代碼了。
座標是可以設置的。OpenGL默認座標系的確是樓上兩位說的那樣,但是我本人更習慣Z軸垂直顯示器平面向內,所以把它修改掉了。
glOrtho(-1, 1, -1, 1, 1, -1); // 默認情形
glOrtho(-1, 1, -1, 1, -1, 1); // 我設置的情形

這裏只給出了繪製的部分,其它部分大家可以自行完成。
void myDisplay(void)
{
// 定義一些材質顏色
const static GLfloat red_color[] = {1.0f, 0.0f, 0.0f, 1.0f};
const static GLfloat green_color[] = {0.0f, 1.0f, 0.0f, 0.3333f};
const static GLfloat blue_color[] = {0.0f, 0.0f, 1.0f, 0.5f};

// 清除屏幕
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// 啓動混合並設置混合因子
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// 設置光源
setLight();

// 以(0, 0, 0.5)爲中心,繪製一個半徑爲.3的不透明紅色球體(離觀察者最遠)
setMatirial(red_color, 30.0);
glPushMatrix();
glTranslatef(0.0f, 0.0f, 0.5f);
glutSolidSphere(0.3, 30, 30);
glPopMatrix();

// 下面將繪製半透明物體了,因此將深度緩衝設置爲只讀
glDepthMask(GL_FALSE);

// 以(0.2, 0, -0.5)爲中心,繪製一個半徑爲.2的半透明藍色球體(離觀察者最近)
setMatirial(blue_color, 30.0);
glPushMatrix();
glTranslatef(0.2f, 0.0f, -0.5f);
glutSolidSphere(0.2, 30, 30);
glPopMatrix();

// 以(0.1, 0, 0)爲中心,繪製一個半徑爲.15的半透明綠色球體(在前兩個球體之間)
setMatirial(green_color, 30.0);
glPushMatrix();
glTranslatef(0.1, 0, 0);
glutSolidSphere(0.15, 30, 30);
glPopMatrix();

// 完成半透明物體的繪製,將深度緩衝區恢復爲可讀可寫的形式
glDepthMask(GL_TRUE);

glutSwapBuffers();
}

大家也可以將上面兩處glDepthMask刪去,結果會看到最近的藍色球雖然是半透明的,但它的背後直接就是紅色球了,中間的綠色球沒有被正確繪製。



小結:
本課介紹了OpenGL混合功能的相關知識。
混合就是在繪製時,不是直接把新的顏色覆蓋在原來舊的顏色上,而是將新的顏色與舊的顏色經過一定的運算,從而產生新的顏色。新的顏色稱爲源顏色,原來舊的顏色稱爲目標顏色。傳統意義上的混合,是將源顏色乘以源因子,目標顏色乘以目標因子,然後相加。
源 因子和目標因子是可以設置的。源因子和目標因子設置的不同直接導致混合結果的不同。將源顏色的alpha值作爲源因子,用1.0減去源顏色alpha值作 爲目標因子,是一種常用的方式。這時候,源顏色的alpha值相當於“不透明度”的作用。利用這一特點可以繪製出一些半透明的物體。
在進行混合時,繪製的順序十分重要。因爲在繪製時,正要繪製上去的是源顏色,原來存在的是目標顏色,因此先繪製的物體就成爲目標顏色,後來繪製的則成爲源顏色。繪製的順序要考慮清楚,將目標顏色和設置的目標因子相對應,源顏色和設置的源因子相對應。
在進行三維混合時,不僅要考慮源因子和目標因子,還應該考慮深度緩衝區。必須先繪製所有不透明的物體,再繪製半透明的物體。在繪製半透明物體時前,還需要將深度緩衝區設置爲只讀形式,否則可能出現畫面錯誤。




簡單的透明
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            : Boolean;          // 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 則意味着完全不透明。
}

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的顏色格式。
               }
發佈了21 篇原創文章 · 獲贊 11 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章