1. 程式人生 > >OpenGL中的光照、材質等屬性

OpenGL中的光照、材質等屬性

OpenGL在處理光照時把光照系統分為三部分,分別是光源、材質和光照模型。

光源、材質和光照模式都有各自的屬性,儘管屬性種類繁多,但這些屬性都只用很少的幾個函式來設定。

使用glLight*函式可設定光源的屬性,

使用glMaterial*函式可設定材質的屬性,

使用glLightModel*函式可設定光照模式。

GL_AMBIENTGL_DIFFUSEGL_SPECULAR這三種屬性是光源和材質所共有的,如果某光源發出的光線照射到某材質的表面,則最終的漫反射強度由兩個GL_DIFFUSE屬性共同決定,最終的鏡面反射強度由兩個GL_SPECULAR屬性共同決定。

OpenGL

中,僅僅支援有限數量的光源。使用GL_LIGHT0表示第0號光源,GL_LIGHT1表示第1號光源,依次類推,OpenGL至少會支援8個光源,即GL_LIGHT0GL_LIGHT7。使用glEnable函式可以開啟它們。例如,glEnable(GL_LIGHT0);可以開啟第0號光源。使用glDisable函式則可以關閉光源。一些OpenGL實現可能支援更多數量的光源,但總的來說,開啟過多的光源將會導致程式執行速度的嚴重下降,

光源GL_LIGHT0與其他幾個光源不同,其他光源的預設顏色時黑色。

GL_DIFFUSE,GL_SPECULAR的預設值是(1.01.0,1.0,1.0

而其他光源的預設值是 (0.0,0.0,0.0,1.0

)。

為了在場景中增加光照,需要執行如下步驟:

1建立和選擇一個或多個光源,並設定它們的位置。

2定義每個物體的每個頂點的法線向量,這些法線決定了物體相對於光源的方向。

3定義場景中物體的材質屬性

4建立和選擇一種光照模型(lighting model),它定義了全域性環境光的層次以及觀察點的有效位置(便於進行光照計算)

1建立、定位和啟用光源設定

光源具有幾種屬性,例如顏色、位置和方向。

設定環境光

glLightfv(GL_LIGHT0,GL_AMBIENT,ambientLight);

設定漫射光成分

glLightfv(GL_LIGHT0,GL_DIFFUSE,DiffuseLight)

設定鏡面光成分

glLightfv(GL_LIGHT0,GL_SPECULAR,SpecularLight);

光源的屬性GL_SPECULAR影響鏡面反射區域的顏色,一般物體的鏡面反射區域的顏色為入射光線的顏色,要實現真實感,應該將它的值設定成與GL_DIFFUSE相同。

環境光(ambient light

環境光是那些在環境中進行了充分的散射,無法分辨器方向的光,它似乎是來自所有方向。沒有特定的方向。環境光的光線充滿著整個場景。場景中的物體都被環境光從各個方向照射著。環境光的特點是:照射在物體上的光來自周圍各個方向,又均勻地向各個方向反射。

image

 漫反射光(diffuse light

漫放射光是一組來自特定方向,具有方向性的光。根據入射光線的角度在表面上均勻地向各個方向反射。因此,如果從正面照射表面,它看起來顯得更亮一些。反之,如果它斜著掠過表面,它看起來就顯得暗一些。漫反射的特點是:光源來自一個方向,反射光均勻地射向各個方向。漫反射光采用點光源照射物體。點光源是位於空間某個位置的一個點,向周圍所有的方向上輻射等光強的光。在點光源的照射下,物體表面的不同部分亮度不同,亮度的大小依賴於它的朝向以及它與點光源之間的距離。

image

鏡面光(specular light)

鏡面光與漫反射光一樣是具有方向性的。高強度的鏡面光會在被照射的物體的表面上形成亮點。對於理想的高光澤度反射面,反射角等於入射角時,光線才會被反射,即只有在等於入射角的反射角方向上,觀察者才能看到反射光。對於這種理想的反射面,鏡面反射的光強要比環境光和漫反射的光強高出很多倍,這時,如果觀察者正好處在P點的鏡面反射方向上,就會看到一個比周圍亮得多的高光點。

image

    除了環境、散射和鏡面顏色之外,材料還可能具有一種發射顏色(emissive color),它模擬那些源自某個物體的光。在OpenGL光照模型中,表面的發射顏色可以增加物體的強度,但是它不受任何光源的影響。另外,在整體場景中,發射顏色並沒有座位一種額外的光照。


設定光源的位置

可以選擇讓光源位於無限遠處,也可以讓它靠近場景。第一種型別稱為方向性光源。由於光源位於無限遠處,當光線到達物體表面時,可以認為所有的光線都是平行的。方向性光源的一個現實世界的例子就是太陽。第二種型別稱為位置性光源,它決定了光線的方向。檯燈就是一個位置性光源的例子。

glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);

GL_POSITION屬性。表示光源所在的位置。由四個值(X, Y, Z, W)表示。

方向性光源(Directional Light) 第四個值W為零,則表示該光源位於無限遠處,前三個值表示了它所在的方向。通常,太陽可以近似的被認為是方向性光源。

位置性光源 (Positional Light) 第四個值W不為零,則X/W, Y/W, Z/W表示了光源的位置。這種光源稱為位置性光源。

下面定義了一個位置在(1,1,1),沒有環境光,鏡面反射光和漫反射光都為白光的光源

GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

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_POSITION, light_position);

glLightfv(GL_LIGHT0, GL_AMBIENT , light_ambient );

glLightfv(GL_LIGHT0, GL_DIFFUSE , light_diffuse );

glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

2 建立聚光燈(這些屬性只對位置性光源有效)

可以對位置性光源的形狀加以限制,使它的發射範圍限定於一個椎體之內,就像聚光燈一樣。

glLightf(GL_LIGHT0,GL_SPOT_CUTOFF,LightCutOff);

GL_SPOT_DIRECTIONGL_SPOT_EXPONENTGL_SPOT_CUTOFF屬性。

表示將光源作為聚光燈使用。很多光源都是向四面八方發射光線,但有時候一些光源則是隻向某個方向發射,比如手電筒,只向一個較小的角度發射光線。

GL_SPOT_DIRECTION 屬性有三個值,表示一個向量,即光源發射的方向。光源的預設方向是(0.0,0.0,-1.0),即指向z軸負方向

GL_SPOT_EXPONENT 屬性只有一個值,表示聚光的程度,為零時表示光照範圍內向各方向發射的光線強度相同,為正數時表示光照向中央集中,正對發射方向的位置受到更多光照,其它位置受到較少光照。數值越大,聚光效果就越明顯。

GL_SPOT_CUTOFF 屬性也只有一個值,表示一個角度,它是光源發射光線所覆蓋角度的一半,其取值範圍在090之間,也可以取180這個特殊值。取值為180時表示光源發射光線覆蓋360度,即不使用聚光燈,向全周圍發射。即一個點光源。

3 設定光線衰減係數(這些屬性只對位置性光源有效)

對於現實世界的光照,隨著光源的距離增加,光的強度也隨之衰減。由於方向性光源位於無限遠處,因此這個原則不使用與方向性光源。但是,我們可以想對位置性光源所發出的光進行衰減。環境光,散射光和鏡面反射光的貢獻都是衰減的,只有發射光和全域性環境光不會衰減。

    設定其位置與設定多邊形頂點的方式相似,各種矩陣變換函式例如:glTranslate*glRotate*等在這裡也同樣有效。方向性光源在計算時比位置性光源快了不少,因此,在視覺效果允許的情況下,應該儘可能的使用方向性光源。

glLightf(GL_LIGHT0,AttenuationWay,SpotAttenuation);

AttenuationWay可以取以下幾個值:

GL_CONSTANT_ATTENUATION -- 表示光線按常熟衰減(與距離無關)

GL_LINEAR_ATTENUATION -- 表示光線按距離線性衰減

GL_QUADRATIC_ATTENUATION -- 表示光線按距離以二次函式衰減。

引數 SpotAttenuation為光線的衰減係數。

GL_CONSTANT_ATTENUATIONGL_LINEAR_ATTENUATIONGL_QUADRATIC_ATTENUATION屬性。這三個屬性表示了光源所發出的光線的直線傳播特性。現實生活中,光線的強度隨著距離的增加而減弱,OpenGL把這個減弱的趨勢抽象成函式:

衰減因子 = 1 / (k1 + k2 * d + k3 * k3 * d)

其中d表示距離,光線的初始強度乘以衰減因子,就得到對應距離的光線強度。k1, k2, k3分別是GL_CONSTANT_ATTENUATION,GL_LINEAR_ATTENUATION,GL_QUADRATIC_ATTENUATION。通過設定這三個常數,就可以控制光線在傳播過程中的減弱趨勢

4 為圖元指定法向量

物體的法線向量決定了它相對於光源的方向。對於物體的每個頂點,OpenGL使用法線判斷這個頂點從每個光源接收的光線數量。為了進行正確的光照計算,表面法線必須為單位長度。還必須保證對物體所進行的模型檢視變換並沒有對錶面法線進行縮放,最終的法線仍然保持為單位長度。為了保證法線仍然為單位長度,可能需要以GL_NORMALIZE或GL_RESCALE_NORMAL為引數呼叫glEnable();

OpenGL必須通過圖元的法線向量來確定圖元的明暗程度

通過計算得到法線向量後,我們需要在繪製頂點前呼叫glNormal函式為頂點或圖元指定歸一化法矢。

使用glTranslate*函式或者glRotate*函式可以改變物體的外觀,但法線向量並不會隨之改變。然而,使用glScale*函式,對每一座標軸進行不同程度的縮放,很有可能導致法線向量的不正確,雖然OpenGL提供了一些措施來修正這一問題,但由此也帶來了各種開銷。因此,在使用了法線向量的場合,應儘量避免使用glScale*函式。即使使用,也最好保證各座標軸進行等比例縮放

對光源進行平移或旋轉,使之相對於靜止的物體移動,這可以在指定模型變換後設置光源位置,然後通過修改模型變換來改變光源的位置。

5 設定材質

    OpenGL 用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顏色。像光源一樣,材料顏色也分成環境、漫反射和鏡面反射成分,它們決定了材料對環境光、漫反射光和鏡面反射光的反射程度。OpenGL的光照方程式僅僅是一種模擬,但它在大部分情況下還是不錯的,並且計算速度較快。在進行光照計算時,材料對環境光的反射率與每個進入光源的環境光結合。對環境光與漫反射光的反射程度決定了材料的顏色,並且它們很相似。對鏡面反射光的反射率通常是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方將變成具有光源鏡面光強度的顏色。例如一個光亮的紅色塑料球,球的大部分表現為紅色,光亮的高光將是白色的。材質的顏色與光源的顏色有些不同。

    對於光源,RGB 值等於RGB 對其最大強度的百分比。若光源顏色的RGB 值都是1.0,則是最強的白光;若值變為0.5,顏色

仍為白色,但強度為原來的一半,於是表現為灰色;若RG1.0B0.0,則光源為黃色。對於材質,RGB 值為材質對光的RGB 成分的反射率。比如,一種材質的R1.0G0.5B0.0,則材質反射全部的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL 的光源顏色為(LRLGLB),材質顏色為(MRMGMB),那麼,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顏色為(LR*MRLG*MGLB*MB

指定了圖元的法線之後,我們還需要為其指定相應的材質以決定物體對各種顏色的光的反射程度,這將影響物體表現為何種顏色

指定材質

glMaterialfv(GL_FRONT,GL_DIFFUSE,@Diffuse);

1 GL_FRONT(正面)GL_BACK(反面)GL_FRONT_AND_BACK(正反兩面)

GL_AMBIENTGL_DIFFUSEGL_SPECULAR屬性。這三個屬性與光源的三個對應屬性類似,每一屬性都由四個值組成。

GL_AMBIENT表示各種光線照射到該材質上,經過很多次反射後最終遺留在環境中的光線強度(顏色)。

GL_DIFFUSE 表示光線照射到該材質上,經過漫反射後形成的光線強度(顏色)。

GL_SPECULAR表示光線照射到該材質上,經過鏡面反射後形成的光線強度(顏色)。

通常,GL_AMBIENTGL_DIFFUSE都取相同的值,可以達到比較真實的效果。使用GL_AMBIENT_AND_DIFFUSE可以同時設定GL_AMBIENTGL_DIFFUSE屬性。

GL_SHININESS屬性。該屬性只有一個值,稱為“鏡面指數”,取值範圍是0128。該值越小,表示材質越粗糙,點光源發射的光線照射到上面,也可以產生較大的亮點。該值越大,表示材質越類似於鏡面,光源照射到上面後,產生較小的亮點。

GL_EMISSION屬性。該屬性由四個值組成,表示一種顏色。OpenGL認為該材質本身就微微的向外發射光線,以至於眼睛感覺到它有這樣的顏色,但這光線又比較微弱,以至於不會影響到其它物體的顏色。

GL_COLOR_INDEXES屬性。該屬性僅在顏色索引模式下使用,由於顏色索引模式下的光照比RGBA模式要複雜,並且使用範圍較小,這裡不做討論。

它們的數值情況如下:

GLfloat earth_mat_ambient[] = { 0.0f, 0.0f, 0.5f, 1.0f};

GLfloat earth_mat_diffuse[] = {0.0f, 0.0f, 0.5f, 1.0f};

GLfloat earth_mat_specular[] = {0.0f, 0.0f, 1.0f, 1.0f};

GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f};

GLfloat earth_mat_shininess = 30.0f;0--128

6 使用顏色跟蹤 (這將導致正面的DIFFUSE總是設定為當前顏色)?

在啟用光照系統之後,為圖元指定顏色變得不太方便。首先我們需要建立一個數組,然後呼叫glMaterial函式將陣列傳給材質,以此決定物體的顏色。為了簡便,我們可以開啟顏色跟蹤來簡化程式碼。呼叫

glEnable(GL_CORLOR_MATERIAL);

啟動顏色跟蹤,再呼叫

glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);

來決定對物體的正面還是反面,對環境光、鏡面光還是漫射光進行顏色跟蹤。

第一個引數可以取

GL_FRONTGL_BACKGL_FRONT_AND_BACK中的任意一種,

第二個引數可以取

GL_AMBIENTGL_DIFFUSEGL_AMBIENT_AND_DIFFUSEGL_SPECULAR中的任意一種。

啟動顏色跟蹤之後,我們就可以像以前一樣,使用glColor函式來指定圖元的顏色了。這時,OpenGL將自動根據從glColor函式傳遞的顏色來決定物體材質,

畫了一個紅色的有立體感的球

glPushMatrix();

 glEnable(GL_COLOR_MATERIAL);

 glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);

 glColor3f(1.0,0.0,0.0);

 glutSolidSphere(1.0, 16, 16);

 glDisable(GL_COLOR_MATERIAL);

 glPopMatrix();

7 光照模型

openGL光照模型的概念由下面4個部分組成:1)全域性環境光強度 2)視點位置在景物附近還是在無窮遠處 3)物體的正面和背面是否分別進行光照計算 4)鏡面顏色是否應該從環境和散射顏色中分離出來,並在紋理操作之後再應用。

光照模型有4部分:

全域性環境光 近視點或遠視點

雙面光照 鏡面反射顏色是否和環境顏色,散射顏色分開。

指定全域性環境光

GLfloat ambient[]={0.3,0.3,0.3,1.0};

glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient);

頂點的鏡面反射亮度取決於該點的法線,頂點相對於光源的方向以及頂點相對於視點的方向。

使用近視點,

glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);

這就將視點放置在(0,0,0).

啟用雙面光照

glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE)

鏡面反射顏色和環境顏色,散射顏色分開

典型的光照計算中,分別計算環境光,散射光,鏡面反射光和發射光的貢獻,然後進行疊加,而在這之後進行紋理對映的話,鏡面反射區可能被覆蓋,為了解決這個問題,

可以glLightModelfv(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR); 這樣,每個頂點光照計算將產生兩種顏色,主顏色和輔助顏色,前者包含所有非鏡面反射光照的貢獻,後者是所有鏡面反射光照的總貢獻。紋理對映的時候只將主顏色和紋理顏色混合起來,執行完紋理對映後,再將主顏色和紋理顏色的混合結果與輔助顏色混合起來。