1. 程式人生 > >OpenGL顏色和光照的應用

OpenGL顏色和光照的應用

繪製一個場景,設定場景中物體的顏色、材質和光照效果(雙光源、鏡面光斑)。

相關演算法及原理描述:

1. 顏色生成原理

計算機顏色不同於繪畫或印刷中的顏色,顯示於計算機螢幕上每一個點的顏色都是由監視器內部的電子槍激發的三束不同顏色的光(紅、綠、藍)混合而成,因此,計算機顏色通 常用R(Red)、G(Green)、B(Blue)三個值來表示,這三個值又稱為顏色分量。

2 .RGB顏色立方體

所有監視器螢幕的顏色都屬於RGB顏色空間,如果用一個立方體形象地表示RGB顏色組成關係,那麼就稱這個立方體為RGB色立體。

3.顏色模式

OpenGL顏色模式一共有兩個:RGB(RGBA)模式和顏色表模式。在RGB模式下,所有的顏色定義全用R、G、B三個值來表示,有時也加上Alpha值(與透明度有關),即RGBA模式。而在顏色表模式下,每一個象素的顏色是用顏色表中的某個顏色索引值表示,而這個索引值指向了相應的R、G、B值。這樣的一個表成為顏色對映(Color Map)。

3.1RGB模式

在RGBA模式下,可以用glColor*()來定義當前顏色。其函式形式為:

  void glColor<x><t> (red, green, blue, alpha);  

    voidglColor<x><t>v(TYPE *v);
第一種函式表示形式中的x表示引數的數目,當它等於3的時候,三個引數分別代表R、G、B值,alpha值預設為1.0;當它等於4的時候,還包括了Alpha值,其範圍從0.0到1.0。函式名中的t指定引數資料的型別,可以取b、d、f、i、s、ub、ui或us,它們分別代表位元組型、雙精度型、浮點型、整型、短整型、無符號位元組型和無符號短整型。

  第二種函式表示形式中的字尾v,表示用一個數組來放置這些引數。

常用的指定顏色的函式是glColor3f,其中每個顏色分量的值在[0.0,1.0]範圍內。

還有一個函式glColor3ub,這個版本使用的顏色分量的取值範圍是0到255之間的無符號位元組,與Windows的RGB巨集指定顏色的方法類似:

         glColor3ub( 128,128,128) =RGB( 128,128,128)

3.2顏色表模式(Color_Index Mode)

採用顏色表模式,可以同時顯示的顏色由顏色對映的尺寸和位平面數目確定。若顏色對映的尺寸為2n,位平面數為m,則可用顏色為min(2n,2m)

在顏色表模式下,可以呼叫glIndex*()函式從顏色表中選取當前顏色。其函式形式為:

              void glIndex{sifd}(TYPE c);

         void glIndex{sifd}v(TYPE *c);

其中,引數值c用於設定當前顏色索引值,即調色盤號,若值大於顏色位面數時則取模。

4.光照模型

4.1簡單光照模型

當光照射到一個物體表面上時,會出現三種情形。首先,光可以通過物體表面向空間反射,產生反射光;其次,對於透明物體,光可以穿透該物體並從另一端射出,產生透射光;最後,部分光被物體表面吸收而轉換成熱。在上述三部分光中,僅僅是透射光和反射光能夠進入人眼產生視覺效果。此外,物體本身還有可能發光,比如發光的燈泡。這裡暫時不考慮透明物體,這樣場景中可能存在以下幾種型別的光,即環境光、漫射光、鏡面光和輻射光。

Phong光照明模型的綜合表述:由物體表面上一點P反射到視點的光強I為環境光的反射光強Ie、理想漫反射光強Id和鏡面反射光Is的總和。


4.2建立光源

OpenGL可提供8個獨立的光源,它們可以放在場景中的任何地方,也可放在視見空間之外。當將光源放在無窮遠處就可以得到平行的光線。

            光源有許多特性,如顏色、位置、方向等。選擇不同的特性值,則對應的光源作用在物體上的效果也不一樣。

            下面詳細講述定義光源特性的函式glLight*():
void glLight{if}[v](GLenum light , GLenum pname, TYPE param)
  建立具有某種特性的光源。其中第一個引數light指定所建立的光源號,如GL_LIGHT0、GL_LIGHT1、...、GL_LIGHT7。第二個引數pname指定光源特性,這個引數的輔助資訊見後表所示。最後一個引數設定相應的光源特性值。

漫反射

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

     glLightfv(GL_LIGHT0,GL_DIFFUSE, light0_diffuse);

         函式glLightfv函式的第一個引數是一個符號常數,指定設定的是哪個光源的引數。第二個引數可以選擇GL_DIFFUSE、GL_AMBIENT、GL_SPECULAR,分別用於指定漫射光、環境光和鏡面光成分。第三個引數是一個包含四個浮點數的陣列,用於指定相應的引數。這段程式碼指定的光源LIGHT0只包含了漫射光成分,是一個漫反射光源。

設定光源位置

 指定了光源成分之後,需要指定光源的位置,如下面一段程式碼:

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

    glLightfv(GL_LIGHT0,GL_POSITION,light0_position);

           這段程式碼指定了光源LIGHT0所在的位置,指定光源位置引數的陣列light0_position包含四個值,其中最後一個值為1.0時,則表明指定的座標就是光源的位置。如果最後一個值是0.0,則表明沿陣列指定的向量方向的光源位於無限遠的地方,即光源發出的光是平行光。

聚光源

光源可以定義成聚光燈形式,即將光的形狀限制在一個圓錐內。OpenGL中聚光的定義包括以下幾步:

       (1) 定義聚光源位置

   (2) 定義聚光截止角

   (3) 定義聚光方向

   (4) 定義聚光指數

衰減因子

OpenGL的光衰減是通過光源的發光量乘以衰減因子計算出來的。常用的衰減因子有常值因子(GL_CONSTANT_ATTENUATION)、線形因子(GL_LINEAR_ATTENUATION)和二次因子(GL_QUADRATIC_ATTENUATION),其中預設的常數衰減因子是1.0,其餘兩個因子都是0.0。

           使用者也可以自己定義這些值,如:

       glLightf(GL_LIGHT0,GL_CONSTANT_ATTENUATION, 2.0);

       glLightf(GL_LIGHT0,GL_LINEAR_ATTENUATION, 1.0);

       glLightf(GL_LIGHT0,GL_QUADRATIC_ATTENUATION, 0.5);

啟動光照

讓OpenGL進行光照計算,必須明確指出光照是否有效或無效。如果光照無效,OpenGL只是簡單地將當前顏色對映到當前頂點上去,而不進行法向、光源、材質等複雜計算。要使光照有效,首先得啟動光照,即: 

              glEnable(GL_LIGHTING); 

    若使光照無效,則呼叫

            gDisable(GL_LIGHTING)

     可關閉當前光照。

明暗處理

在計算機圖形學中,光滑的曲面表面常用多邊形予以逼近和表示,而每個小多邊形輪廓(或內部)就用單一的顏色或許多不同的顏色來勾畫(或填充),這種處理方式就稱為明暗處理。在OpenGL中,用單一顏色處理的稱為平面明暗處理(Flat Shading),用許多不同顏色處理的稱為光滑明暗處理(Smooth Shading),也稱為Gourand明暗處理(Gourand Shading)。設定明暗處理模式的函式為:
  void glShadeModel(GLenum mode);
  函式引數為GL_FLAT或GL_SMOOTH,分別表示平面明暗處理和光滑明暗處理。

用平面明暗處理模式時,多邊形內每個點的法向一致,且顏色也一致;應用光滑明暗處理模式時,多邊形所有點的法向是由內插生成的,具有一定的連續性,因此每個點的顏色也相應內插,故呈現不同色。這種模式下,插值方法採用的是雙線性插值法,如圖所示。
4.3材質屬性

設定材質屬性

材質的定義與光源的定義類似。其函式為:

void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
  定義光照計算中用到的當前材質。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明當前材質應該應用到物體的哪一個面上;pname說明一個特定的材質;param是材質的具體數值,若函式為向量形式,則param是一組值的指標。

下面的程式碼指定了後面繪製的多邊形都具有相同的材質屬性:

       GLfloatmaterial_ambiemt[]={0.75f, 0.75f, 0.75f, 1.0f};

     glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE, material_ambiemt);

       glMaterialfv函式的第一個引數指定多邊形的前面(GL_FRONT),背面(GL_BACK)或者兩面(GL_FRONT_AND_BACK)具有指定的材質屬性。第二個引數則指定當前設定的是哪一種材質屬性,這裡使用GL_AMBIENT_AND_DIFFUSE,即設定環境光和漫反射光的值。最後一個引數是一個數組,它包含了構成屬性的RGBA值。

鏡面光斑

 在定義了光源中的鏡面光成分之後(函式glLightfv函式的第二個引數設定成GL_SPECULAR ),物體上並不會產生鏡面光斑的效果,還需要定義材質的鏡面反射率和鏡面指數。

(1) 鏡面反射率

       鏡面反射率決定了材質表面對於鏡面光反射的成分,如下面的程式碼:

       GLfloat mat_specular[] = {1.0f, 1.0f, 1.0f, 1.0f };

       glMaterialfv(GL_FRONT,GL_SPECULAR, mat_specular);

       指定了其後繪製的表面幾乎反射了所有的入射鏡面光。

(2)鏡面指數

       鏡面指數說明如何確定鏡面光亮斑的大小和聚光程度。值為0時指定一個沒有焦點的鏡面光亮斑,它能夠對整個多邊形的顏色均勻加亮。如果增大這個值,可以減少鏡面光亮斑的尺寸、增加聚焦度,就出現了閃亮的光斑,該引數值的範圍在[1,128]之間。

     下面的程式碼指定材質的鏡面指數。

       glMaterialfv(GL_FRONT,GL_SHININESS, 50);

輻射光

輻射光可以使物體看起來發出某種顏色的光(自發光),通過給GL_EMISSION定義一個RGBA值,可以達到這種特殊效果。可以利用這一特性來模擬燈和其他光源。程式碼如下:

              GLfloat mat_emission[]={0.3f, 0.3f, 0.5f, 0.0f};

              glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission);

         這樣,物體看起來稍微有點發光。比如繪製一個開啟的檯燈,就可以將一個小球的材質定義成上述形式,並且在小球內部建立一個聚光源,這樣檯燈的燈泡效果就出來了。

 程式運用矩陣堆疊多次呼叫glMaterialfv()來設定每個球的材質,建議最好儘可能少的改變材質,以減少改變材質時所帶來的效能開銷。更常用的設定材質屬性的方法是使用顏色跟蹤法。相應函式為glColorMaterial(),說明如下:

    void glColorMaterial(GLenumface,GLenum mode);

引數face指定面,值有GL_FRONT、GL_BACK或GL_FRONT_AND_BACK(預設值)。

mode指定材質成分,值有GL_AMBIENT、GL_DIFFUSE、GL_AMBIENT_AND_DIFFUSE(預設值)、GL_SPECULAR或GLEMISSION。
在呼叫glColorMterial()以後,必須呼叫帶有引數GL_COLOR_MATERIAL的glEnable函式來啟動顏色材質,然後通過呼叫glColor就可以設定材質顏色屬性,或用glMaterial()來改變材質成分。當不用這種方式來改變材質時,可呼叫glDisable(GL_COLOR_MATERIAL)來關閉取消。

示例:顏色定義改變材質

材質RGB值和光源RGB值的關係

在進行光照計算時,物體的最終顏色是由其材質屬性的RGB值和光照屬性的RGB值共同決定的。也就是說,若OpenGL的光源顏色為(LR、LG、LB),材質顏色為(MR、MG、MB),那麼,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顏色為(LR*MR、LG*MG、LB*MB), 即將每個環境光源的成分與材質的環境反射率相乘。

             例如,如果當前的環境光源的RGB值為(0.5,1.0,0.5),而物體的材質的環境反射成分的RGB值為(0.5,0.5,0.5),那麼物體最終的顏色為:

                (0.5×0.5,1.0×0.5,0.5×0.5)=(0.25,0.5,0.25)

            同樣,如果有兩束光,相應的值分別為(R1、G1、B1)和(R2、G2、B2),則OpenGL將各個顏色成分相加,得到(R1+R2、G1+G2、B1+B2),即當疊加的RGB中任何一個顏色分量的值大於1.0,那麼就用1.0計算。

設定光照模型

 允許進行光照計算後,需要設定光照模型,影響光照模型的三個引數是通過glLightModel函式中設定的。

              //設定光照

              glEnable(GL_LIGHTING);

              GLfloat ambient[]={0.8f, 0.8f, 0.8f, 1.0f};

              glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);

            在上面的示例中,使用了引數GL_LIGHT_MODEL_AMBIENT,這個引數允許指定當前場景的全域性環境光,它可以從各邊均勻照射所有的物體。第二個引數為一個儲存RGBA值的陣列。注意,預設的全域性環境光的RGBA值為(0.2,0.2,0.2,1.0),光線相當黯淡的。

雙面光照

光照計算通常是對所有多邊形進行的,無論其是正面或反面。一般情況下,設定光照條件時總是正面多邊形,因此不能對背面多邊形進行正確地光照。對於一個封閉的物體,只有正面多邊形能看到,這種情況下不必考慮背面光照。而如果物體不封閉,其內部的曲面是可見的,那麼對內部多邊形需進行光照計算,這時應該呼叫如下函式啟動雙面光照:

              glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_TRUE);

        要關閉雙面光照,只需將引數GL_TRUE改為GL_FALSE即可。