目 錄 10.1
概念 10.2
光照模型 10.3
明暗處理 10.4
材質本章是比較重要的一部分,前幾章只是基礎,在這章里將著重講述光照模型、明暗處理、光源設置、材質定義以及有關的計算機圖形學概念等內容,介紹如何運用OpenGL函數來繪制真實感圖形,充分展示三維圖形世界的魅力。如果讀者以前從未接觸過這方面的概念,要直接應用OpenGL函數庫編寫三維圖形程序,是很困難的。因此,我們將結合OpenGL的特殊性簡潔明了地介紹一些有關真實感圖形繪制的概念,幫助讀者理解基本的OpenGL光照、材質等函數的應用方式,順利掌握最基本的真實感圖形編程方法,引導讀者快速進入OpenGL三維圖形世界。
10.1、真實感圖形基本概念 真實感圖形繪制是計算機圖形學的一個重要組成部分,它綜合利用數學、物理學、計算機科學和其它科學知識在計算機圖形設備上生成象彩色照片那樣的具有真實感的圖形。一般說來,用計算機在圖形設備上生成真實感圖形必須完成以下四個步驟:一是用建模,即用一定的數學方法建立所需三維場景的幾何描述,場景的幾何描述直接影響圖形的復雜性和圖形繪制的計算耗費;二是將三維幾何模型經過一定變換轉為二維平面透視投影圖;三是確定場景中所有可見面,運用隱藏面消隱算法將視域外或被遮擋住的不可見面消去;四是計算場景中可見面的顏色,即根據基于光學物理的光照模型計算可見面投射到觀察者眼中的光亮度大小和顏色分量,并將它轉換成適合圖形設備的顏色值,從而確定投影畫面上每一象素的顏色,最終生成圖形。
由于真實感圖形是通過景物表面的顏色和明暗色調來表現景物的幾何形狀、空間位置以及表面材料的,而一個物體表面所呈現的顏色是由表面向視線方向輻射的光能決定的。在計算機圖形學中,常采用一個既能表示光能大小又能表示其顏色組成的物理量即光亮度(
luminance)或光強(
intensity of light)來描述物體表面朝某方向輻射光能的顏色。采用這個物理量可以正確描述光在物體表面的反射、透射和吸收現象,因而可以正確計算處物體表面在空間給定方向上的光能顏色。
物體表面向空間給定方向輻射的光強可應用光照模型進行計算。簡單的光照模型通常假定物體表面是光滑的且由理想材料構成,因此只考慮光源照射在物體表面產生的反射光,所生成的圖形可以模擬處不透明物體表面的明暗過渡,具有一定的真實感效果。復雜的光照模型除了考慮上述因素外,還要考慮周圍環境的光對物體表面的影響。如光亮平滑的物體表面會將環境中其它物體映像在表面上,而通過透明物體也可看到其后的環境景象。這類光照模型稱為整體光照模型,它能模擬出鏡面映像、透明等較精致的光照效果。為了更真實的繪制圖形,還要考慮物體表面的細節紋理,這通常使用一種稱為“紋理映射”(
texture mapping)的技術把已有的平面花紋圖案映射到物體表面上,并在應用光照模型時將這些花紋的顏色考慮進去,物體表面細節的模擬使繪制的圖形更接近自然景物。
以上內容中,真實感圖形繪制的四大步驟前兩步在前面的章節已經詳細介紹過,這里不再重復,第三步OpenGL將自動完成所有消隱過程,第四步下面幾節詳述。另外,部分復雜光照模型應用將在后續章節里介紹。
10.2、光照模型 10.2.1 簡單光照模型 當光照射到一個物體表面上時,會出現三種情形。首先,光可以通過物體表面向空間反射,產生反射光。其次,對于透明體,光可以穿透該物體并從另一端射出,產生透射光。最后,部分光將被物體表面吸收而轉換成熱。在上述三部分光中,僅僅是透射光和反射光能夠進入人眼產生視覺效果。這里介紹的簡單光照模型只考慮被照明物體表面的反射光影響,假定物體表面光滑不透明且由理想材料構成,環境假設為由白光照明。
一般來說,反射光可以分成三個分量,即環境反射、漫反射和鏡面反射。環境反射分量假定入射光均勻地從周圍環境入射至景物表面并等量地向各個方向反射出去,通常物體表面還會受到從周圍環境來的反射光(如來自地面、天空、墻壁等的反射光)的照射,這些光常統稱為環境光(
Ambient Light);漫反射分量表示特定光源在景物表面的反射光中那些向空間各方向均勻反射出去的光,這些光常稱為漫射光(
Diffuse Light);鏡面反射光為朝一定方向的反射光,如一個點光源照射一個金屬球時會在球面上形成一塊特別亮的區域,呈現所謂“高光(
Highlight)”,它是光源在金屬球面上產生的鏡面反射光(
Specular Light)。對于較光滑物體,其鏡面反射光的高光區域小而亮;相反,粗糙表面的鏡面反射光呈發散狀態,其高光區域大而不亮。下面先看一個簡單的光照例程。
例10-1 簡單光照例程(
light0.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auxSolidSphere(1.0);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Simple Lighting");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是顯示一個具有灰色光影的球。其中函數myinit()中包含了關鍵的設定光源位置、啟動光照等幾句,而其它程序語言幾乎與以前的沒有多大區別,但效果卻完全不一樣。下面幾個小節將詳細介紹有關函數的用法。
 |
圖10-1 帶光影的灰色球體 |
10.2.2 OpenGL光組成
在OpenGL簡單光照模型中的幾種光分為:輻射光(Emitted Light)、環境光(Ambient Light)、漫射光(Diffuse Light)、鏡面光(Specular Light)。
輻射光是最簡單的一種光,它直接從物體發出并且不受任何光源影響。
環境光是由光源發出經環境多次散射而無法確定其方向的光,即似乎來自所有方向。一般說來,房間里的環境光成分要多些,戶外的相反要少得多,因為大部分光按相同方向照射,而且在戶外很少有其他物體反射的光。當環境光照到曲面上時,它在各個方向上均等地發散(類似于無影燈光)。
漫射光來自一個方向,它垂直于物體時比傾斜時更明亮。一旦它照射到物體上,則在各個方向上均勻地發散出去。于是,無論視點在哪里它都一樣亮。來自特定位置和特定方向的任何光,都可能有散射成分。
鏡面光來自特定方向并沿另一方向反射出去,一個平行激光束在高質量的鏡面上產生100%的鏡面反射。光亮的金屬和塑料具有很高非反射成分,而象粉筆和地毯等幾乎沒有反射成分。因此,三某種意義上講,物體的反射程度等同于其上的光強(或光亮度)。
10.2.3 創建光源(Light Source)
光源有許多特性,如顏色、位置、方向等。選擇不同的特性值,則對應的光源作用在物體上的效果也不一樣,這在以后的章節中會逐步介紹的。下面詳細講述定義光源特性的函數glLight*(): void glLight{if}[v](GLenum light , GLenum pname, TYPE param)
創建具有某種特性的光源。其中第一個參數light指定所創建的光源號,如GL_LIGHT0、GL_LIGHT1、...、GL_LIGHT7。第二個參數pname指定光源特性,這個參數的輔助信息見表10-1所示。最后一個參數設置相應的光源特性值。
pname 參數名 |
缺省值 |
說明 |
GL_AMBIENT |
(0.0, 0.0, 0.0, 1.0) |
RGBA模式下環境光 |
GL_DIFFUSE |
(1.0, 1.0, 1.0, 1.0) |
RGBA模式下漫反射光 |
GL_SPECULAR |
(1.0,1.0,1.0,1.0) |
RGBA模式下鏡面光 |
GL_POSITION |
(0.0,0.0,1.0,0.0) |
光源位置齊次坐標(x,y,z,w) |
GL_SPOT_DIRECTION |
(0.0,0.0,-1.0) |
點光源聚光方向矢量(x,y,z) |
GL_SPOT_EXPONENT |
0.0 |
點光源聚光指數 |
GL_SPOT_CUTOFF |
180.0 |
點光源聚光截止角 |
GL_CONSTANT_ATTENUATION |
1.0 |
常數衰減因子 |
GL_LINER_ATTENUATION |
0.0 |
線性衰減因子 |
GL_QUADRATIC_ATTENUATION |
0.0 |
平方衰減因子 |
表10-1 函數glLight*()參數pname說明 |
注意:以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用于GL_LIGHT0,其他幾個光源的GL_DIFFUSE和GL_SPECULAR缺省值為(0.0,0.0,0.0,1.0)。另外,表中后六個參數的應用放在下一篇中介紹。在上面例程中,光源的創建為:
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
其中light_position是一個指針,指向定義的光源位置齊次坐標數組。其它幾個光源特性都為缺省值。同樣,我們也可用類似的方式定義光源的其他幾個特性值,例如:
GLfloat light_ambient [] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse [] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient );
glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse );
glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
10.2.4 啟動光照
在OpenGL中,必須明確指出光照是否有效或無效。如果光照無效,則只是簡單地將當前顏色映射到當前頂點上去,不進行法向、光源、材質等復雜計算,那么顯示的圖形就沒有真實感,如前幾章例程運行結果顯示。要使光照有效,首先得啟動光照,即:
glEnable(GL_LIGHTING);
若使光照無效,則調用gDisable(GL_LIGHTING)可關閉當前光照。然后,必須使所定義的每個光源有效,例light0.c中只用了一個光源,即:
glEnable(GL_LIGHT0);
其它光源類似,只是光源號不同而已。
10.3、明暗處理
在計算機圖形學中,光滑的曲面表面常用多邊形予以逼近和表示,而每個小多邊形輪廓(或內部)就用單一的顏色或許多不同的顏色來勾畫(或填充),這種處理方式就稱為明暗處理。在OpenGL中,用單一顏色處理的稱為平面明暗處理(Flat Shading),用許多不同顏色處理的稱為光滑明暗處理(Smooth Shading),也稱為Gourand明暗處理(Gourand Shading)。設置明暗處理模式的函數為:
void glShadeModel(GLenum mode);
函數參數為GL_FLAT或GL_SMOOTH,分別表示平面明暗處理和光滑明暗處理。
應用平面明暗處理模式時,多邊形內每個點的法向一致,且顏色也一致;應用光滑明暗處理模式時,多邊形所有點的法向是由內插生成的,具有一定的連續性,因此每個點的顏色也相應內插,故呈現不同色。這種模式下,插值方法采用的是雙線性插值法,如圖10-2所示。
 |
圖10-2 Gouraud明暗處理 |
Gouraud明暗處理通常算法為:先用多邊形頂點的光強線性插值出當前掃描線與多邊形邊交點處的光強,然后再用交點的光強線插值處掃描線位于多邊形內區段上每一象素處的光強值。圖中顯示出一條掃描線與多邊形相交,交線的端點是A點和B點,P點是掃描線上位于多邊形內的任一點,多邊形三個頂點的光強分別為I1、I2和I3.取A點的光強Ia為I1和I2的線性插值,B點的光強Ib為I1和I3的線性插值,P點的光強Ip則為Ia和Ib的線性插值。采用Gouraud明暗處理不但可以使用多邊形表示的曲面光強連續,而且計算量很小。這種算法還可以以增量的形式改進,且能用硬件直接實現算法,從而廣泛用于計算機實時圖形生成。請看下面光滑明暗處理的例程:
例10-2 明暗處理例程(Shading.c) #include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void object(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
/* GL_SMOOTH is actually the default shading model. */
void myinit (void)
{
glShadeModel (GL_SMOOTH);
}
void object(void)
{
glBegin (GL_POLYGON);
glColor3f (1.0, 0.0, 0.0);
glVertex2f (4.0, 4.0);
glColor3f(1.0,1.0,1.0);
glVertex2f (12.0, 4.0);
glColor3f(0.0,0.0,1.0);
glVertex2f (12.0, 12.0);
glColor3f(0.0,1.0,0.0);
glVertex2f (4.0, 12.0);
glEnd ();
}
void CALLBACK display(void)
{
glClear (GL_COLOR_BUFFER_BIT);
object ();
glFlush ();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
gluOrtho2D (0.0, 16.0, 0.0, 16.0 * (GLfloat) h/(GLfloat) w);
else
gluOrtho2D (0.0, 16.0 * (GLfloat) w/(GLfloat) h, 0.0, 16.0);
glMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Smooth Shading");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是在屏幕上顯示一個色彩連續變化的三角形。這個程序是用的RGBA顯示模式,若改用顏色表模式,則顏色內插實際上是顏色表的內插,因此呈現的顏色可能不連續。網友不妨自己試試。
另外,若在light0.c程序中加上一句定義GL_FLAT明暗處理模式,則又會出現怎樣的情形呢?讀者可以仔細比較一下。
 |
圖10-3 高氏明暗處理的正方形 |
10.4、材質
10.4.1 材質顏色
OpenGL用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顏色。象光源一樣,材料顏色也分成環境、漫反射和鏡面反射成分,它們決定了材料對環境光、漫反射光和鏡面反射光的反射程度。在進行光照計算時,材料對環境光的反射率與每個進入光源的環境光結合,對漫反射光的反射率與每個進入光源的漫反射光結合,對鏡面光的反射率與每個進入光源的鏡面反射光結合。對環境光與漫反射光的反射程度決定了材料的顏色,并且它們很相似。對鏡面反射光的反射率通常是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方將變成具有光源鏡面光強度的顏色。例如一個光亮的紅色塑料球,球的大部分表現為紅色,光亮的高光將是白色的。
10.4.2 材質定義
材質的定義與光源的定義類似。其函數為:
void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
定義光照計算中用到的當前材質。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明當前材質應該應用到物體的哪一個面上;pname說明一個特定的材質;param是材質的具體數值,若函數為向量形式,則param是一組值的指針,反之為參數值本身。非向量形式僅用于設置GL_SHINESS。pname參數值具體內容見表10-1。另外,參數GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值設置環境光顏色和漫反射光顏色。
參數名 |
缺省值 |
說明 |
GL_AMBIENT |
(0.2, 0.2, 0.2, 1.0) |
材料的環境光顏色 |
GL_DIFFUSE |
(0.8, 0.8, 0.8, 1.0) |
材料的漫反射光顏色 |
GL_AMBIENT_AND_DIFFUSE |
|
材料的環境光和漫反射光顏色 |
GL_SPECULAR |
(0.0, 0.0, 0.0, 1.0) |
材料的鏡面反射光顏色 |
GL_SHINESS |
0.0 |
鏡面指數(光亮度) |
GL_EMISSION |
(0.0, 0.0, 0.0, 1.0) |
材料的輻射光顏色 |
GL_COLOR_INDEXES |
(0, 1, 1) |
材料的環境光、漫反射光和鏡面光顏色 |
表10-2 函數glMaterial*()參數pname的缺省值 |
例10-3 材質定義例程(light1.c) #include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void); void myinit(void)
{
/* 設置材質的各種光的顏色成分反射比率 */
GLfloat mat_ambient[]={0.8,0.8,0.8,1.0};
GLfloat mat_diffuse[]={0.8,0.0,0.8,1.0}; /* 紫色 */
GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.0 }; /* 亮紫色 */
GLfloat mat_shininess[] = { 50.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auxSolidSphere(1.0);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH16);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Lighting_1 ");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是一個紫色的球。在函數myinit()中定義了球的材質顏色,光源的定義仍延用light0.c中的,而light.c物體的光源定義為缺省形式。從例子中明顯地看出,物體的材質顏色定義與光源顏色定義幾乎一樣,物體反射到眼中的顏色與二者都有關系,具體關系請看下一小節。
10.4.3 材質RGB值和光源RGB值的關系
材質的顏色與光源的顏色有些不同。對于光源,R、G、B值等于R、G、B對其最大強度的百分比。若光源顏色的R、G、B值都是1.0,則是最強的白光;若值變為0.5,顏色仍為白色,但強度為原來的一半,于是表現為灰色;若R=G=1.0,B=0.0,則光源為黃色。對于材質,R、G、B值為材質對光的R、G、B成分的反射率。比如,一種材質的R=1.0、G=0.5、B=0.0,則材質反射全部的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL的光源顏色為(LR、LG、LB),材質顏色為(MR、MG、MB),那么,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顏色為(LR*MR、LG*MG、LB*MB)。
同樣,如果有兩束光,相應的值分別為(R1、G1、B1)和(R2、G2、B2),則OpenGL將各個顏色成分相加,得到(R1+R2、G1+G2、B1+B2),若任一成分的和值大于1(超出了設備所能顯示的亮度)則約簡到1.0。下面一例程就說明了二者之間的關系。
例10-4 材質與光源的RGB關系例程(light2.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
GLfloat mat_ambient[]= { 0.8, 0.8, 0.8, 1.0 };
GLfloat mat_diffuse[]= { 0.8, 0.0, 0.8, 1.0 }; /* 紫色 */
GLfloat mat_specular[] = { 1.0, 0.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat light_diffuse[]= { 0.0, 0.0, 1.0, 1.0}; /* 藍色 */
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auxSolidSphere(1.0);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH16);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("Lighting_2 ");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序運行結果是一個藍色的球,其中高光部分仍為上一例的亮紫色。從上可看出,球漫反射光的結果是mat_diffuse[]與light_diffuse[]中的三個顏色分量值相乘,即 (0.0*1.0,0.0*1.0,0.8*1.0,1.0*1.0)=(0.0,0.0,0.8,1.0),所以球大部分呈現藍色。
 |
圖10-4 光照藍色球(高光為紅色) |
10.4.4 材質改變
在實際應用的許多情況下,不同的物體或同一物體的不同部分都有可能設置不同的材質,OpenGL函數庫提供了兩種方式實現這種要求。下面一例程采用的是設置矩陣堆棧來保存不同物體的材質信息:
例10-5 矩陣堆棧改變材質例程(chgmat1.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK display(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
/* 初始化z-buffer、光源和光照模型,在此不具體定義材質。*/
void myinit(void)
{
GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat position[] = { 0.0, 3.0, 2.0, 0.0 };
GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_POSITION, position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glClearColor(0.0, 0.1, 0.1, 0.0);
}
void CALLBACK display(void)
{
GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 };
GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 };
GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0 };
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat no_shininess[] = { 0.0 };
GLfloat low_shininess[] = { 5.0 };
GLfloat high_shininess[] = { 100.0 };
GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* 第一行第一列繪制的球僅有漫反射光而無環境光和鏡面光。*/
glPushMatrix();
glTranslatef (-3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第一行第二列繪制的球有漫反射光和鏡面光,并有低高光,而無環境光 。*/
glPushMatrix();
glTranslatef (-1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第一行第三列繪制的球有漫反射光和鏡面光,并有很亮的高光,而無環境光 。*/
glPushMatrix();
glTranslatef (1.25, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第一行第四列繪制的球有漫反射光和輻射光,而無環境和鏡面反射光。*/
glPushMatrix();
glTranslatef (3.75, 3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
auxSolidSphere(1.0);
glPopMatrix();
/* 第二行第一列繪制的球有漫反射光和環境光,而鏡面反射光。*/
glPushMatrix();
glTranslatef (-3.75, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第二行第二列繪制的球有漫反射光、環境光和鏡面光,且有低高光。*/
glPushMatrix();
glTranslatef (-1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第二行第三列繪制的球有漫反射光、環境光和鏡面光,且有很亮的高光。*/
glPushMatrix();
glTranslatef (1.25, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第二行第四列繪制的球有漫反射光、環境光和輻射光,而無鏡面光。*/
glPushMatrix();
glTranslatef (3.75, 0.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
auxSolidSphere(1.0); glPopMatrix();
/* 第三行第一列繪制的球有漫反射光和有顏色的環境光,而無鏡面光。*/
glPushMatrix();
glTranslatef (-3.75, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第三行第二列繪制的球有漫反射光和有顏色的環境光以及鏡面光,且有低高光。*/
glPushMatrix();
glTranslatef (-1.25, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第三行第三列繪制的球有漫反射光和有顏色的環境光以及鏡面光,且有很亮的高光。*/
glPushMatrix();
glTranslatef (1.25, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, no_mat);
auxSolidSphere(1.0);
glPopMatrix();
/* 第三行第四列繪制的球有漫反射光和有顏色的環境光以及輻射光,而無鏡面光。*/
glPushMatrix();
glTranslatef (3.75, -3.0, 0.0);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient_color);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);
auxSolidSphere(1.0);
glPopMatrix();
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= (h * 2))
glOrtho (-6.0, 6.0, -3.0*((GLfloat)h*2)/(GLfloat)w,
3.0*((GLfloat)h*2)/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-6.0*(GLfloat)w/((GLfloat)h*2),
6.0*(GLfloat)w/((GLfloat)h*2), -3.0, 3.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGBA);
auxInitPosition (0, 0, 600, 450);
auxInitWindow ("Material");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
 |
圖10-5 多種光和材質的變化效果 |
以上程序運行結果是繪制12個球(3行4列)。第一行的球材質都沒有環境反射光,第二行的都有一定的環境反射光,第三行的都有某種顏色的環境光。而第一列的球材質僅有藍色的漫反射光;第二列的不僅有藍漫反射光,而且還有鏡面反射光,較低的高光;第三列的不僅有藍漫反射光,而且還有鏡面反射光,很亮的高光;第四列的還包括輻射光,但無鏡面光。
這個程序運用矩陣堆棧多次調用glMaterialfv()來設置每個球的材質,也就是改變同一場景中的不同物體的顏色。但由于這個函數的應用有個性能開銷,因此建議最好盡可能少的改變材質,以減少改變材質時所帶來的性能開銷,可采用另一種方式即改變材質顏色,相應函數為glColorMaterial(),說明如下: void glColorMaterial(GLenum face,GLenum mode);
函數參數face指定面,值有GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(缺省值)。mode指定材質成分,值有GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(缺省值)、GL_SPECULAR或GLEMISSION。
注意:這個函數說明了兩個獨立的值,第一個參數說明哪一個面和哪些面被修改,而第二個參數說明這些面的哪一個或哪些材質成分要被修改。OpenGL并不為每一種face保持獨立的mode變量。在調用glColorMterial()以后,首先需要用GL_COLOR_MATERIAL作為參數調用glEnable()來啟動顏色材質,然后在繪圖時調用glColor*()來改變當前顏色,或用glMaterial()來改變材質成分。當不用這種方式來改變材質時,可調用glDisable(GL_COLOR_MATERIAL)來關閉取消。如下面一段代碼:
glColorMaterial(GL_FRONT,GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glColor3f(0.3,0.5,0.7);
/* draw some objects here. */
glcolor3f(0.0,1.0,0.0);
/* draw other objects here.*/
glDisable(GL_COLOR_MATERIAL);
當需要改變場景中大部分方面的單個材質時,最好調用glColorMaterial();當需要修改不止一個材質參數時,最好調用glMaterial*()。注意,當不需要顏色材質時一定要關閉它,以避免相應的開銷。下面來看一個顏色材質的具體應用例子:
例10-6 顏色定義改變材質例程(chgmat2.c)
#include "glos.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w, GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glColorMaterial(GL_FRONT, GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* draw one yellow ball */
glLoadIdentity();
glTranslatef(-0.7,0.0,0.0);
glColor3f(1.0,1.0,0.0);
auxSolidSphere(0.5);
/* draw one red cone */
glLoadIdentity();
glRotatef(-65.0,1.0,0.0,0.0);
glTranslatef(0.7,0.0,0.0);
glColor3f(1.0,0.0,0.0);
auxSolidCone(0.4,0.6);
glFlush();
}
void CALLBACK myReshape(GLsizei w, GLsizei h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void main(void)
{
auxInitDisplayMode (AUX_SINGLE | AUX_RGB | AUX_DEPTH16);
auxInitPosition (0, 0, 500, 500);
auxInitWindow ("ColorMaterial Mode");
myinit();
auxReshapeFunc (myReshape);
auxMainLoop(display);
}
以上程序改變的是漫反射顏色。場景中顯示了一個黃色的球和一個紅色的錐體。
 |
圖10-6 漫反射材質改變 |