第八課
     
 

簡單的透明

OpenGL 中大多數的特效都依賴在一些混色的技巧上. 混色用來結合一個指定像素點的顏色, 而這個像素點之前就已經存在於畫面上了. 顏色的結合方式是基於顏色上的透明度值, 以及/或是混色所使用的公式. 透明度是第四個色彩元件, 通常指定在最後的位置. 之前你用過 GL_RGB 來指定色彩的三個要素. GL_RGBA 就可以用來指定透明度. 另外, 我們可以用 glColor4f() 而不是 glColor3f().

大多數人認為透明度就是物質不透明的程度. 透明度的值為 0.0f 表示物質是完全透明的. 1.0f 表示物質是完全不透明的.

混色公式

如果你不熟悉數學, 那就只要看看透明如何運作就夠了, 跳過這一段吧. 如果你想了解混色如何運作, 這一段就是為你設計的.

(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)

OpenGL 將會用以上的公式計算出兩個像素點混色後的結果. 符號 s 和 r 表示來源和目的像素點. 元件 S 和 D 是混色的因素. 這些值指出你要如何混色. 大多數一般的 S 和 D 值分別為 (As, As, As, As) (又叫做來源透明度) 是指 S, 而 (1, 1, 1, 1) - (As, As, As, As) (又叫做一減去來源透明度) 是指 D. 這會產生混色公式看起來像這樣:

(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bs (1 - As), As As + Ad (1 - As))

這個公式會產生透明/半透明類型的特效.

在 OpenGL 中混色

我們就像一般其他狀況一樣的啟動混色. 然後我們設定公式, 並且在畫透明物件時關閉深度緩衝區, 那我們還是可以在畫物件在畫了透明圖案之後. 這並不是正確的混色方法, 但是大多時候在簡單的程式裡, 它可以運作的很正常.

Rui Martins Adds: 正確畫透明多邊形的方法, 是在畫完整個場景的不透明多邊形後, 再依照反向的深度順序 (先畫最遠的) 畫出透明物件.

在混色兩個多邊形 (編號 1 和 編號 2) 時用不同的順序會導致不同的結果, 例如: 假設一號多邊形較靠近觀察者, 那正確的繪製方法是先畫二號多邊形再畫一號多邊形. 如果你看著它時, 就會很真實的, 所有來自這兩個多邊形 (都是透明的多邊形) 後方的光源, 在到達觀察者的眼睛之前, 會先通過二號多邊形, 再通過一號多邊形.

你應該把透明多邊形依照深度排序, 並且在畫完整個場景的不透明多邊形後再畫它們, 此時深度緩衝區也應該開啟著, 否則你會得到錯誤的結果. 我知道有時候這會很痛苦的, 不過這是運作的正確方法.


我們會用第七課的程式碼. 在程式一開始的上面加入兩個新的變數. 我將會重寫這一段程式碼好讓它更清楚些.
 
     
#include <windows.h>					// Header File For Windows
#include <stdio.h>					// Header File For Standard Input/Output
#include <gl\gl.h>					// Header File For The OpenGL32 Library
#include <gl\glu.h>					// Header File For The GLu32 Library
#include <gl\glaux.h>					// Header File For The GLaux Library

HDC		hDC=NULL;				// Private GDI Device Context
HGLRC		hRC=NULL;				// Permanent Rendering Context
HWND		hWnd=NULL;				// Holds Our Window Handle
HINSTANCE	hInstance;				// Holds The Instance Of The Application

bool	keys[256];					// Array Used For The Keyboard Routine
bool	active=TRUE;					// Window Active Flag Set To TRUE By Default
bool	fullscreen=TRUE;				// Fullscreen Flag Set To Fullscreen Mode By Default
bool	light;						// Lighting ON/OFF
bool	blend;						// Blending OFF/ON? ( NEW )
bool	lp;						// L Pressed?
bool	fp;						// F Pressed?
bool	bp;						// B Pressed? ( NEW )

GLfloat	xrot;						// X Rotation
GLfloat	yrot;						// Y Rotation
GLfloat	xspeed;						// X Rotation Speed
GLfloat	yspeed;						// Y Rotation Speed

GLfloat	z=-5.0f;					// Depth Into The Screen

GLfloat LightAmbient[]=  { 0.5f, 0.5f, 0.5f, 1.0f };	// Ambient Light Values
GLfloat LightDiffuse[]=	 { 1.0f, 1.0f, 1.0f, 1.0f };	// Diffuse Light Values
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };	// Light Position

GLuint	filter;						// Which Filter To Use
GLuint	texture[3];					// Storage for 3 textures

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Declaration For WndProc
     
  移到 LoadGLTextures() 程序中. 找到這一行: if (TextureImage[0]=LoadBMP("Data/Crate.bmp")). 把它改為下列這一行. 我們將用一個彩繪玻璃類型的貼圖材質在這課中, 而不是用木箱材質.  
     
	texture1 = auxDIBImageLoad("Data/glass.bmp");	// Load The Glass Bitmap ( MODIFIED )
     
  加入下面這兩行程式碼在 InitGL() 程式碼段落中. 這行要做的就是設定繪製物件的亮度為全亮度以及 50% 的透明度 (半透明). 這表示混色被開啟了, 物件將有 50% 的透明度. 第二行是設定我們所要用的混色類型.

Rui Martins Adds: 一個透明度值為 0.0f 表示物質是完全透明的. 值為 1.0f 表示物質是完全不透明.
 
     
	glColor4f(1.0f,1.0f,1.0f,0.5f);			// Full Brightness, 50% Alpha ( NEW )
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);		// Blending Function For Translucency Based On Source Alpha Value ( NEW )
     
  看看這一段程式碼, 可以在第七課的最後找到它.  
     
	if (keys[VK_LEFT])				// Is Left Arrow Being Pressed?
	{
		yspeed-=0.01f;				// If So, Decrease yspeed
	}
     
  在上面程式碼的後面, 我們想加入接下來這幾行. 下列你看到的是否 'B' 按鍵被按下. 如果 'B' 按鍵被按下, 電腦就會檢查看看是否混色功能被開啟或是關閉. 如果混色已經被開啟, 那電腦就會把它關閉; 如果混色已經被關閉, 那電腦就會把它開啟.  
     
	if (keys['B'] && !bp)				// Is B Key Pressed And bp FALSE?
	{
		bp=TRUE;				// If So, bp Becomes TRUE
		blend = !blend;				// Toggle blend TRUE / FALSE	
		if(blend)				// Is blend TRUE?
		{
			glEnable(GL_BLEND);		// Turn Blending On
			glDisable(GL_DEPTH_TEST);	// Turn Depth Testing Off
		}
		else					// Otherwise
		{
			glDisable(GL_BLEND);		// Turn Blending Off
			glEnable(GL_DEPTH_TEST);	// Turn Depth Testing On
		}
	}
	if (!keys['B'])					// Has B Key Been Released?
	{
		bp=FALSE;				// If So, bp Becomes FALSE
	}
     
  但是在使用貼圖材質時, 我們要如何指定色彩呢? 很簡單, 在調整貼圖模式中, 每一個像素點會被乘上目前的色彩. 所以如果要畫出來的色彩是 (0.5, 0.6, 0.4), 我們乘上色彩之後, 就會得到 (0.5, 0.6, 0.4, 0.2) (透明度在沒有給定時會被假設為 1.0)

那就對了! 混色在 OpenGL 中的確是很簡單的.

注意 (11/13/99)

我 ( NeHe ) 已經修改了混色的程式碼, 所以物件顯示出的會更像它應該顯示的樣子. 在來源圖素和目的圖素中, 同時使用透明度值會讓混色看起來像是人工產生的. 導致後面的面在沿著表面邊緣看起來會比較暗. 基本上物件看起來會很奇怪. 我所用的混色方法可能不是最好的, 但是它是可以運作, 而且當光源啟動時, 物件看起來也蠻像它應該顯示的樣子. 謝謝 Tom 初始化了這個程式碼, 他用的混色方法是混合透明度值的適當方法, 不過它看起來不夠吸引人 :)

程式碼再一次的修改是因為定址的問題, 有些顯示卡在 glDepthMask() 會有問題. 這個指令在某些顯示卡上, 似乎不能有效的開啟和關閉深度緩衝區測試, 所以我把它改用早期的深度測試的 glEnable 和 Disable.

貼圖材質的透明度.

透明度值用在貼圖材質上的和一般色彩的一樣, 這個要運作的話, 你將需要在載入圖像時也指定它的透明度資料, 然後在呼叫 glTexImage2D() 時使用 GL_RGBA 這種色彩格式.

問題?

如果你有任何問題, 請直接和我聯絡 stanis@cs.wisc.edu.

Tom Stanis

* 下載 Visual C++ 程式碼給本課程的.
* 下載 Visual Fortran 程式碼給本課程的. ( Conversion by Jean-Philippe Perois )
* 下載 Delphi 程式碼給本課程的. ( Conversion by Marc Aarts )
* 下載 Linux 程式碼給本課程的. ( Conversion by Richard Campbell )
* 下載 Irix 程式碼給本課程的. ( Conversion by Lakmal Gunasekara )
* 下載 Solaris 程式碼給本課程的. ( Conversion by Lakmal Gunasekara )
* 下載 Mac OS 程式碼給本課程的. ( Conversion by Anthony Parker )
* 下載 Power Basic 程式碼給本課程的. ( Conversion by Angus Law )
* 下載 BeOS 程式碼給本課程的. ( Conversion by Chris Herborth )
* 下載 Java 程式碼給本課程的. ( Conversion by Darren Hodges )
* 下載 MingW32 & Allegro 程式碼給本課程的. ( Conversion by Peter Puck )
* 下載 Borland C++ Builder 4.0 程式碼給本課程的. ( Conversion by Patrick Salmons )
 
     
 
Back To NeHe Productions!
回到 OpenGL 教學索引
中文版由 Macbear 翻譯