目 錄 10.1
概念 10.2
光照模型 10.3
明暗處理 10.4
材質(zhì)本章是比較重要的一部分,前幾章只是基礎(chǔ),在這章里將著重講述光照模型、明暗處理、光源設(shè)置、材質(zhì)定義以及有關(guān)的計(jì)算機(jī)圖形學(xué)概念等內(nèi)容,介紹如何運(yùn)用OpenGL函數(shù)來繪制真實(shí)感圖形,充分展示三維圖形世界的魅力。如果讀者以前從未接觸過這方面的概念,要直接應(yīng)用OpenGL函數(shù)庫編寫三維圖形程序,是很困難的。因此,我們將結(jié)合OpenGL的特殊性簡潔明了地介紹一些有關(guān)真實(shí)感圖形繪制的概念,幫助讀者理解基本的OpenGL光照、材質(zhì)等函數(shù)的應(yīng)用方式,順利掌握最基本的真實(shí)感圖形編程方法,引導(dǎo)讀者快速進(jìn)入OpenGL三維圖形世界。
10.1、真實(shí)感圖形基本概念 真實(shí)感圖形繪制是計(jì)算機(jī)圖形學(xué)的一個(gè)重要組成部分,它綜合利用數(shù)學(xué)、物理學(xué)、計(jì)算機(jī)科學(xué)和其它科學(xué)知識(shí)在計(jì)算機(jī)圖形設(shè)備上生成象彩色照片那樣的具有真實(shí)感的圖形。一般說來,用計(jì)算機(jī)在圖形設(shè)備上生成真實(shí)感圖形必須完成以下四個(gè)步驟:一是用建模,即用一定的數(shù)學(xué)方法建立所需三維場景的幾何描述,場景的幾何描述直接影響圖形的復(fù)雜性和圖形繪制的計(jì)算耗費(fèi);二是將三維幾何模型經(jīng)過一定變換轉(zhuǎn)為二維平面透視投影圖;三是確定場景中所有可見面,運(yùn)用隱藏面消隱算法將視域外或被遮擋住的不可見面消去;四是計(jì)算場景中可見面的顏色,即根據(jù)基于光學(xué)物理的光照模型計(jì)算可見面投射到觀察者眼中的光亮度大小和顏色分量,并將它轉(zhuǎn)換成適合圖形設(shè)備的顏色值,從而確定投影畫面上每一象素的顏色,最終生成圖形。
由于真實(shí)感圖形是通過景物表面的顏色和明暗色調(diào)來表現(xiàn)景物的幾何形狀、空間位置以及表面材料的,而一個(gè)物體表面所呈現(xiàn)的顏色是由表面向視線方向輻射的光能決定的。在計(jì)算機(jī)圖形學(xué)中,常采用一個(gè)既能表示光能大小又能表示其顏色組成的物理量即光亮度(
luminance)或光強(qiáng)(
intensity of light)來描述物體表面朝某方向輻射光能的顏色。采用這個(gè)物理量可以正確描述光在物體表面的反射、透射和吸收現(xiàn)象,因而可以正確計(jì)算處物體表面在空間給定方向上的光能顏色。
物體表面向空間給定方向輻射的光強(qiáng)可應(yīng)用光照模型進(jìn)行計(jì)算。簡單的光照模型通常假定物體表面是光滑的且由理想材料構(gòu)成,因此只考慮光源照射在物體表面產(chǎn)生的反射光,所生成的圖形可以模擬處不透明物體表面的明暗過渡,具有一定的真實(shí)感效果。復(fù)雜的光照模型除了考慮上述因素外,還要考慮周圍環(huán)境的光對(duì)物體表面的影響。如光亮平滑的物體表面會(huì)將環(huán)境中其它物體映像在表面上,而通過透明物體也可看到其后的環(huán)境景象。這類光照模型稱為整體光照模型,它能模擬出鏡面映像、透明等較精致的光照效果。為了更真實(shí)的繪制圖形,還要考慮物體表面的細(xì)節(jié)紋理,這通常使用一種稱為“紋理映射”(
texture mapping)的技術(shù)把已有的平面花紋圖案映射到物體表面上,并在應(yīng)用光照模型時(shí)將這些花紋的顏色考慮進(jìn)去,物體表面細(xì)節(jié)的模擬使繪制的圖形更接近自然景物。
以上內(nèi)容中,真實(shí)感圖形繪制的四大步驟前兩步在前面的章節(jié)已經(jīng)詳細(xì)介紹過,這里不再重復(fù),第三步OpenGL將自動(dòng)完成所有消隱過程,第四步下面幾節(jié)詳述。另外,部分復(fù)雜光照模型應(yīng)用將在后續(xù)章節(jié)里介紹。
10.2、光照模型 10.2.1 簡單光照模型 當(dāng)光照射到一個(gè)物體表面上時(shí),會(huì)出現(xiàn)三種情形。首先,光可以通過物體表面向空間反射,產(chǎn)生反射光。其次,對(duì)于透明體,光可以穿透該物體并從另一端射出,產(chǎn)生透射光。最后,部分光將被物體表面吸收而轉(zhuǎn)換成熱。在上述三部分光中,僅僅是透射光和反射光能夠進(jìn)入人眼產(chǎn)生視覺效果。這里介紹的簡單光照模型只考慮被照明物體表面的反射光影響,假定物體表面光滑不透明且由理想材料構(gòu)成,環(huán)境假設(shè)為由白光照明。
一般來說,反射光可以分成三個(gè)分量,即環(huán)境反射、漫反射和鏡面反射。環(huán)境反射分量假定入射光均勻地從周圍環(huán)境入射至景物表面并等量地向各個(gè)方向反射出去,通常物體表面還會(huì)受到從周圍環(huán)境來的反射光(如來自地面、天空、墻壁等的反射光)的照射,這些光常統(tǒng)稱為環(huán)境光(
Ambient Light);漫反射分量表示特定光源在景物表面的反射光中那些向空間各方向均勻反射出去的光,這些光常稱為漫射光(
Diffuse Light);鏡面反射光為朝一定方向的反射光,如一個(gè)點(diǎn)光源照射一個(gè)金屬球時(shí)會(huì)在球面上形成一塊特別亮的區(qū)域,呈現(xiàn)所謂“高光(
Highlight)”,它是光源在金屬球面上產(chǎn)生的鏡面反射光(
Specular Light)。對(duì)于較光滑物體,其鏡面反射光的高光區(qū)域小而亮;相反,粗糙表面的鏡面反射光呈發(fā)散狀態(tài),其高光區(qū)域大而不亮。下面先看一個(gè)簡單的光照例程。
例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);
}
以上程序運(yùn)行結(jié)果是顯示一個(gè)具有灰色光影的球。其中函數(shù)myinit()中包含了關(guān)鍵的設(shè)定光源位置、啟動(dòng)光照等幾句,而其它程序語言幾乎與以前的沒有多大區(qū)別,但效果卻完全不一樣。下面幾個(gè)小節(jié)將詳細(xì)介紹有關(guān)函數(shù)的用法。
 |
圖10-1 帶光影的灰色球體 |
10.2.2 OpenGL光組成
在OpenGL簡單光照模型中的幾種光分為:輻射光(Emitted Light)、環(huán)境光(Ambient Light)、漫射光(Diffuse Light)、鏡面光(Specular Light)。
輻射光是最簡單的一種光,它直接從物體發(fā)出并且不受任何光源影響。
環(huán)境光是由光源發(fā)出經(jīng)環(huán)境多次散射而無法確定其方向的光,即似乎來自所有方向。一般說來,房間里的環(huán)境光成分要多些,戶外的相反要少得多,因?yàn)榇蟛糠止獍聪嗤较蛘丈洌以趹敉夂苌儆衅渌矬w反射的光。當(dāng)環(huán)境光照到曲面上時(shí),它在各個(gè)方向上均等地發(fā)散(類似于無影燈光)。
漫射光來自一個(gè)方向,它垂直于物體時(shí)比傾斜時(shí)更明亮。一旦它照射到物體上,則在各個(gè)方向上均勻地發(fā)散出去。于是,無論視點(diǎn)在哪里它都一樣亮。來自特定位置和特定方向的任何光,都可能有散射成分。
鏡面光來自特定方向并沿另一方向反射出去,一個(gè)平行激光束在高質(zhì)量的鏡面上產(chǎn)生100%的鏡面反射。光亮的金屬和塑料具有很高非反射成分,而象粉筆和地毯等幾乎沒有反射成分。因此,三某種意義上講,物體的反射程度等同于其上的光強(qiáng)(或光亮度)。
10.2.3 創(chuàng)建光源(Light Source)
光源有許多特性,如顏色、位置、方向等。選擇不同的特性值,則對(duì)應(yīng)的光源作用在物體上的效果也不一樣,這在以后的章節(jié)中會(huì)逐步介紹的。下面詳細(xì)講述定義光源特性的函數(shù)glLight*(): void glLight{if}[v](GLenum light , GLenum pname, TYPE param)
創(chuàng)建具有某種特性的光源。其中第一個(gè)參數(shù)light指定所創(chuàng)建的光源號(hào),如GL_LIGHT0、GL_LIGHT1、...、GL_LIGHT7。第二個(gè)參數(shù)pname指定光源特性,這個(gè)參數(shù)的輔助信息見表10-1所示。最后一個(gè)參數(shù)設(shè)置相應(yīng)的光源特性值。
pname 參數(shù)名 |
缺省值 |
說明 |
GL_AMBIENT |
(0.0, 0.0, 0.0, 1.0) |
RGBA模式下環(huán)境光 |
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) |
光源位置齊次坐標(biāo)(x,y,z,w) |
GL_SPOT_DIRECTION |
(0.0,0.0,-1.0) |
點(diǎn)光源聚光方向矢量(x,y,z) |
GL_SPOT_EXPONENT |
0.0 |
點(diǎn)光源聚光指數(shù) |
GL_SPOT_CUTOFF |
180.0 |
點(diǎn)光源聚光截止角 |
GL_CONSTANT_ATTENUATION |
1.0 |
常數(shù)衰減因子 |
GL_LINER_ATTENUATION |
0.0 |
線性衰減因子 |
GL_QUADRATIC_ATTENUATION |
0.0 |
平方衰減因子 |
表10-1 函數(shù)glLight*()參數(shù)pname說明 |
注意:以上列出的GL_DIFFUSE和GL_SPECULAR的缺省值只能用于GL_LIGHT0,其他幾個(gè)光源的GL_DIFFUSE和GL_SPECULAR缺省值為(0.0,0.0,0.0,1.0)。另外,表中后六個(gè)參數(shù)的應(yīng)用放在下一篇中介紹。在上面例程中,光源的創(chuàng)建為:
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
其中l(wèi)ight_position是一個(gè)指針,指向定義的光源位置齊次坐標(biāo)數(shù)組。其它幾個(gè)光源特性都為缺省值。同樣,我們也可用類似的方式定義光源的其他幾個(gè)特性值,例如:
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 啟動(dòng)光照
在OpenGL中,必須明確指出光照是否有效或無效。如果光照無效,則只是簡單地將當(dāng)前顏色映射到當(dāng)前頂點(diǎn)上去,不進(jìn)行法向、光源、材質(zhì)等復(fù)雜計(jì)算,那么顯示的圖形就沒有真實(shí)感,如前幾章例程運(yùn)行結(jié)果顯示。要使光照有效,首先得啟動(dòng)光照,即:
glEnable(GL_LIGHTING);
若使光照無效,則調(diào)用gDisable(GL_LIGHTING)可關(guān)閉當(dāng)前光照。然后,必須使所定義的每個(gè)光源有效,例light0.c中只用了一個(gè)光源,即:
glEnable(GL_LIGHT0);
其它光源類似,只是光源號(hào)不同而已。
10.3、明暗處理
在計(jì)算機(jī)圖形學(xué)中,光滑的曲面表面常用多邊形予以逼近和表示,而每個(gè)小多邊形輪廓(或內(nèi)部)就用單一的顏色或許多不同的顏色來勾畫(或填充),這種處理方式就稱為明暗處理。在OpenGL中,用單一顏色處理的稱為平面明暗處理(Flat Shading),用許多不同顏色處理的稱為光滑明暗處理(Smooth Shading),也稱為Gourand明暗處理(Gourand Shading)。設(shè)置明暗處理模式的函數(shù)為:
void glShadeModel(GLenum mode);
函數(shù)參數(shù)為GL_FLAT或GL_SMOOTH,分別表示平面明暗處理和光滑明暗處理。
應(yīng)用平面明暗處理模式時(shí),多邊形內(nèi)每個(gè)點(diǎn)的法向一致,且顏色也一致;應(yīng)用光滑明暗處理模式時(shí),多邊形所有點(diǎn)的法向是由內(nèi)插生成的,具有一定的連續(xù)性,因此每個(gè)點(diǎn)的顏色也相應(yīng)內(nèi)插,故呈現(xiàn)不同色。這種模式下,插值方法采用的是雙線性插值法,如圖10-2所示。
 |
圖10-2 Gouraud明暗處理 |
Gouraud明暗處理通常算法為:先用多邊形頂點(diǎn)的光強(qiáng)線性插值出當(dāng)前掃描線與多邊形邊交點(diǎn)處的光強(qiáng),然后再用交點(diǎn)的光強(qiáng)線插值處掃描線位于多邊形內(nèi)區(qū)段上每一象素處的光強(qiáng)值。圖中顯示出一條掃描線與多邊形相交,交線的端點(diǎn)是A點(diǎn)和B點(diǎn),P點(diǎn)是掃描線上位于多邊形內(nèi)的任一點(diǎn),多邊形三個(gè)頂點(diǎn)的光強(qiáng)分別為I1、I2和I3.取A點(diǎn)的光強(qiáng)Ia為I1和I2的線性插值,B點(diǎn)的光強(qiáng)Ib為I1和I3的線性插值,P點(diǎn)的光強(qiáng)Ip則為Ia和Ib的線性插值。采用Gouraud明暗處理不但可以使用多邊形表示的曲面光強(qiáng)連續(xù),而且計(jì)算量很小。這種算法還可以以增量的形式改進(jìn),且能用硬件直接實(shí)現(xiàn)算法,從而廣泛用于計(jì)算機(jī)實(shí)時(shí)圖形生成。請(qǐng)看下面光滑明暗處理的例程:
例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);
}
以上程序運(yùn)行結(jié)果是在屏幕上顯示一個(gè)色彩連續(xù)變化的三角形。這個(gè)程序是用的RGBA顯示模式,若改用顏色表模式,則顏色內(nèi)插實(shí)際上是顏色表的內(nèi)插,因此呈現(xiàn)的顏色可能不連續(xù)。網(wǎng)友不妨自己試試。
另外,若在light0.c程序中加上一句定義GL_FLAT明暗處理模式,則又會(huì)出現(xiàn)怎樣的情形呢?讀者可以仔細(xì)比較一下。
 |
圖10-3 高氏明暗處理的正方形 |
10.4、材質(zhì)
10.4.1 材質(zhì)顏色
OpenGL用材料對(duì)光的紅、綠、藍(lán)三原色的反射率來近似定義材料的顏色。象光源一樣,材料顏色也分成環(huán)境、漫反射和鏡面反射成分,它們決定了材料對(duì)環(huán)境光、漫反射光和鏡面反射光的反射程度。在進(jìn)行光照計(jì)算時(shí),材料對(duì)環(huán)境光的反射率與每個(gè)進(jìn)入光源的環(huán)境光結(jié)合,對(duì)漫反射光的反射率與每個(gè)進(jìn)入光源的漫反射光結(jié)合,對(duì)鏡面光的反射率與每個(gè)進(jìn)入光源的鏡面反射光結(jié)合。對(duì)環(huán)境光與漫反射光的反射程度決定了材料的顏色,并且它們很相似。對(duì)鏡面反射光的反射率通常是白色或灰色(即對(duì)鏡面反射光中紅、綠、藍(lán)的反射率相同)。鏡面反射高光最亮的地方將變成具有光源鏡面光強(qiáng)度的顏色。例如一個(gè)光亮的紅色塑料球,球的大部分表現(xiàn)為紅色,光亮的高光將是白色的。
10.4.2 材質(zhì)定義
材質(zhì)的定義與光源的定義類似。其函數(shù)為:
void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
定義光照計(jì)算中用到的當(dāng)前材質(zhì)。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明當(dāng)前材質(zhì)應(yīng)該應(yīng)用到物體的哪一個(gè)面上;pname說明一個(gè)特定的材質(zhì);param是材質(zhì)的具體數(shù)值,若函數(shù)為向量形式,則param是一組值的指針,反之為參數(shù)值本身。非向量形式僅用于設(shè)置GL_SHINESS。pname參數(shù)值具體內(nèi)容見表10-1。另外,參數(shù)GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值設(shè)置環(huán)境光顏色和漫反射光顏色。
參數(shù)名 |
缺省值 |
說明 |
GL_AMBIENT |
(0.2, 0.2, 0.2, 1.0) |
材料的環(huán)境光顏色 |
GL_DIFFUSE |
(0.8, 0.8, 0.8, 1.0) |
材料的漫反射光顏色 |
GL_AMBIENT_AND_DIFFUSE |
|
材料的環(huán)境光和漫反射光顏色 |
GL_SPECULAR |
(0.0, 0.0, 0.0, 1.0) |
材料的鏡面反射光顏色 |
GL_SHINESS |
0.0 |
鏡面指數(shù)(光亮度) |
GL_EMISSION |
(0.0, 0.0, 0.0, 1.0) |
材料的輻射光顏色 |
GL_COLOR_INDEXES |
(0, 1, 1) |
材料的環(huán)境光、漫反射光和鏡面光顏色 |
表10-2 函數(shù)glMaterial*()參數(shù)pname的缺省值 |
例10-3 材質(zhì)定義例程(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)
{
/* 設(shè)置材質(zhì)的各種光的顏色成分反射比率 */
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);
}
以上程序運(yùn)行結(jié)果是一個(gè)紫色的球。在函數(shù)myinit()中定義了球的材質(zhì)顏色,光源的定義仍延用light0.c中的,而light.c物體的光源定義為缺省形式。從例子中明顯地看出,物體的材質(zhì)顏色定義與光源顏色定義幾乎一樣,物體反射到眼中的顏色與二者都有關(guān)系,具體關(guān)系請(qǐng)看下一小節(jié)。
10.4.3 材質(zhì)RGB值和光源RGB值的關(guān)系
材質(zhì)的顏色與光源的顏色有些不同。對(duì)于光源,R、G、B值等于R、G、B對(duì)其最大強(qiáng)度的百分比。若光源顏色的R、G、B值都是1.0,則是最強(qiáng)的白光;若值變?yōu)?.5,顏色仍為白色,但強(qiáng)度為原來的一半,于是表現(xiàn)為灰色;若R=G=1.0,B=0.0,則光源為黃色。對(duì)于材質(zhì),R、G、B值為材質(zhì)對(duì)光的R、G、B成分的反射率。比如,一種材質(zhì)的R=1.0、G=0.5、B=0.0,則材質(zhì)反射全部的紅色成分,一半的綠色成分,不反射藍(lán)色成分。也就是說,若OpenGL的光源顏色為(LR、LG、LB),材質(zhì)顏色為(MR、MG、MB),那么,在忽略所有其他反射效果的情況下,最終到達(dá)眼睛的光的顏色為(LR*MR、LG*MG、LB*MB)。
同樣,如果有兩束光,相應(yīng)的值分別為(R1、G1、B1)和(R2、G2、B2),則OpenGL將各個(gè)顏色成分相加,得到(R1+R2、G1+G2、B1+B2),若任一成分的和值大于1(超出了設(shè)備所能顯示的亮度)則約簡到1.0。下面一例程就說明了二者之間的關(guān)系。
例10-4 材質(zhì)與光源的RGB關(guān)系例程(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}; /* 藍(lán)色 */
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);
}
以上程序運(yùn)行結(jié)果是一個(gè)藍(lán)色的球,其中高光部分仍為上一例的亮紫色。從上可看出,球漫反射光的結(jié)果是mat_diffuse[]與light_diffuse[]中的三個(gè)顏色分量值相乘,即 (0.0*1.0,0.0*1.0,0.8*1.0,1.0*1.0)=(0.0,0.0,0.8,1.0),所以球大部分呈現(xiàn)藍(lán)色。
 |
圖10-4 光照藍(lán)色球(高光為紅色) |
10.4.4 材質(zhì)改變
在實(shí)際應(yīng)用的許多情況下,不同的物體或同一物體的不同部分都有可能設(shè)置不同的材質(zhì),OpenGL函數(shù)庫提供了兩種方式實(shí)現(xiàn)這種要求。下面一例程采用的是設(shè)置矩陣堆棧來保存不同物體的材質(zhì)信息:
例10-5 矩陣堆棧改變材質(zhì)例程(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、光源和光照模型,在此不具體定義材質(zhì)。*/
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);
/* 第一行第一列繪制的球僅有漫反射光而無環(huán)境光和鏡面光。*/
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();
/* 第一行第二列繪制的球有漫反射光和鏡面光,并有低高光,而無環(huán)境光 。*/
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();
/* 第一行第三列繪制的球有漫反射光和鏡面光,并有很亮的高光,而無環(huán)境光 。*/
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();
/* 第一行第四列繪制的球有漫反射光和輻射光,而無環(huán)境和鏡面反射光。*/
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();
/* 第二行第一列繪制的球有漫反射光和環(huán)境光,而鏡面反射光。*/
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();
/* 第二行第二列繪制的球有漫反射光、環(huán)境光和鏡面光,且有低高光。*/
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();
/* 第二行第三列繪制的球有漫反射光、環(huán)境光和鏡面光,且有很亮的高光。*/
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();
/* 第二行第四列繪制的球有漫反射光、環(huán)境光和輻射光,而無鏡面光。*/
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();
/* 第三行第一列繪制的球有漫反射光和有顏色的環(huán)境光,而無鏡面光。*/
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();
/* 第三行第二列繪制的球有漫反射光和有顏色的環(huán)境光以及鏡面光,且有低高光。*/
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();
/* 第三行第三列繪制的球有漫反射光和有顏色的環(huán)境光以及鏡面光,且有很亮的高光。*/
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();
/* 第三行第四列繪制的球有漫反射光和有顏色的環(huán)境光以及輻射光,而無鏡面光。*/
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 多種光和材質(zhì)的變化效果 |
以上程序運(yùn)行結(jié)果是繪制12個(gè)球(3行4列)。第一行的球材質(zhì)都沒有環(huán)境反射光,第二行的都有一定的環(huán)境反射光,第三行的都有某種顏色的環(huán)境光。而第一列的球材質(zhì)僅有藍(lán)色的漫反射光;第二列的不僅有藍(lán)漫反射光,而且還有鏡面反射光,較低的高光;第三列的不僅有藍(lán)漫反射光,而且還有鏡面反射光,很亮的高光;第四列的還包括輻射光,但無鏡面光。
這個(gè)程序運(yùn)用矩陣堆棧多次調(diào)用glMaterialfv()來設(shè)置每個(gè)球的材質(zhì),也就是改變同一場景中的不同物體的顏色。但由于這個(gè)函數(shù)的應(yīng)用有個(gè)性能開銷,因此建議最好盡可能少的改變材質(zhì),以減少改變材質(zhì)時(shí)所帶來的性能開銷,可采用另一種方式即改變材質(zhì)顏色,相應(yīng)函數(shù)為glColorMaterial(),說明如下: void glColorMaterial(GLenum face,GLenum mode);
函數(shù)參數(shù)face指定面,值有GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(缺省值)。mode指定材質(zhì)成分,值有GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(缺省值)、GL_SPECULAR或GLEMISSION。
注意:這個(gè)函數(shù)說明了兩個(gè)獨(dú)立的值,第一個(gè)參數(shù)說明哪一個(gè)面和哪些面被修改,而第二個(gè)參數(shù)說明這些面的哪一個(gè)或哪些材質(zhì)成分要被修改。OpenGL并不為每一種face保持獨(dú)立的mode變量。在調(diào)用glColorMterial()以后,首先需要用GL_COLOR_MATERIAL作為參數(shù)調(diào)用glEnable()來啟動(dòng)顏色材質(zhì),然后在繪圖時(shí)調(diào)用glColor*()來改變當(dāng)前顏色,或用glMaterial()來改變材質(zhì)成分。當(dāng)不用這種方式來改變材質(zhì)時(shí),可調(diào)用glDisable(GL_COLOR_MATERIAL)來關(guān)閉取消。如下面一段代碼:
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);
當(dāng)需要改變場景中大部分方面的單個(gè)材質(zhì)時(shí),最好調(diào)用glColorMaterial();當(dāng)需要修改不止一個(gè)材質(zhì)參數(shù)時(shí),最好調(diào)用glMaterial*()。注意,當(dāng)不需要顏色材質(zhì)時(shí)一定要關(guān)閉它,以避免相應(yīng)的開銷。下面來看一個(gè)顏色材質(zhì)的具體應(yīng)用例子:
例10-6 顏色定義改變材質(zhì)例程(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);
}
以上程序改變的是漫反射顏色。場景中顯示了一個(gè)黃色的球和一個(gè)紅色的錐體。
 |
圖10-6 漫反射材質(zhì)改變 |