[轉載]從零開始學習OpenGL ES之五 – 材質

在 上一篇文章,我們討論了光效的設定以及光效的各種屬性。我們還討論了光的三要素:散射光環境光 和高光。如果你還不是完全清楚,那麼我們來複習一下,在定義材質時大量的用到這些要素。


 

作爲本文的起點,我們使用了原文球體繪製 的項目文件。我們不再使用二十面體而是轉向球體是因爲球體是展示光和材質不同要素之間相互作用的最佳形狀。

 

顏色是什麼

 

這可能是對小學美術課的複習。爲什麼現實世界會有顏色?是什麼造成的?

 

我們看得見的光被稱爲 光的可見頻譜 。根據不同的波長我們可以感知到不同的顏色。在可見光譜的一端是低波長高頻率的紫色和藍色,而在另一端是低頻高波長的橘色和紅色:

 

電磁波在這個範圍之外,因此不是“可見光”,儘管這只是人工的區分方法,它們的唯一不同在頻率和波長,在於人眼的感知。儘管有各種方法感知各種電磁波的能力,但從OpenGL的角度出發,我們只關心可見光譜。

 

“白色光”包括等量的所有波長。換言之,白光之所以是白色的是因爲它包括了所有(或至少大部分)可見光的頻率。如果你曾經做過棱鏡試驗,那麼你可能看過如下效果:

 

棱鏡反射白光,各種波長被分離出來。這就是彩虹產生的原理。

 

如果你看到一個物體呈藍色,那麼實際上是該物體吸收了大部分可見光譜低頻部分。它吸收了紅,橘,黃和綠光。根據藍色的不同色度,它還可能吸收一些紫色和藍色。

 

但大部分藍色的波長都被反射到你的眼睛。因爲一些可見光被吸收了,由於它不再包括可見光譜中的所有波長所以反射到你眼中的光不再是白色。

 

簡單吧?讓我們看看這些是怎樣運用在OpenGL的。

 

OpenGL 材質

 

我們通過定義材質的反射光來定義OpenGL ES中的材質,正如現實世界中一樣。如果一個材質定義爲反射紅光,那麼在正常的白光下,它將顯示紅色。

 

在OpenGL中 (至少在使用光滑着色處理和光效時),材質是沒有顏色的。OpenGL具有分別定義材質是怎樣反射OpenGL光效三要素(環境,散射和高光)的能力。另外,它還具有指定材質 自發光(emissive) 屬性的能力,關於這點我們稍後再討論。

 

指定材質

 

要在OpenGL創建一個材質,我們需要一次或多次調用 glMaterialf() 或者 glMaterialfv()。類似於上一篇文章中光效的定義,由於各屬性或元素需要分別通過這些調用了指定,所以我們通常必須多次調用這些函數以完全定義材質。所有未定義的元素或屬性默認值爲0,或者以顏色來說爲黑色。

 

傳遞給 glMaterialf() 或者 glMaterialfv() 的第一個參數總是用於指定是否材質影響多邊形的前,後或兩者的GL_ENUM。實際上除了爲了與OpenGL兼容,第一個參數在OpenGL ES中沒有什麼意義,因爲只有一個有效的選項: GL_FRONT_AND_BACK,它簡單地表示材質適用於任何繪製的多邊形。如果你還記得第一部分,那麼你應該知道三角形的正面和背面是由winding(頂點的繪製次序)決定的。默認情況下,只有三角形的正面被繪製出來,但是有可能讓OpenGL也繪製背面,或甚至只繪製背面,常規 OpenGL 允許你通過傳遞GL_FRONTGL_BACK, 或者 GL_FRONT_AND_BACK來爲正面和背面指定不同的材質。但是OpenGL ES僅支持GL_FRONT_AND_BACK。

 

glMaterialf() 或 glMaterialfv()的第二個參數是指示正在設定材質的哪個元素或屬性的 GL_ENUM。它們像傳遞給glLightfv()的值一樣,比如 GL_AMBIENT ,另外還有些新的值我們稍後再談。

 

最終值是 GL_FLOAT或包括了實際屬性或元素的GL_FLOAT數組的指針。

 

材質的最重要元素是環境光和散射光,因爲它們決定了材質是怎樣反射大量光線的。我們今天使用的項目代碼定義了正如太陽光或白熾燈產生的白色,它具有平均分佈的各種波長和顏色的光。如果光不是白色,球體看上去會有不同的外觀。例如,反射至紅色材質的藍光將產生紫色陰影。簡單起見,我們只使用白色光。當然,你可以隨意改變光的顏色進行試驗看看光和材質是怎樣交互作用的。大部分時候,它們在OpenGL ES中的表示與現實生活中完全一樣。

 

下面是項目在添加材質前運行時的樣子:

 

如你所見,它具有一些環境光和更爲顯著的散射光。

 

環境光和散射光


當討論OpenGL的材質時,我們需要同時討論環境光和散射光,這是因爲這兩個元素是一起工作從而決定物體被感知的顏色的。記住,散射光處於本文第一個圖片中球體的頂部(亮黃色),環境光則是下方的暗黃色。材質怎樣反射這兩個元素決定了物體被感知的顏色。上圖的效果可以通過不止一種方法獲得。同樣地,黃色球以同樣比例反射環境光和散射光,但是場景中具有較少的環境光。

 

大約 90% 或更多的情況下,將材質的環境光和散射光參數設定成一樣。這樣做,使它們成爲決定物體陰影和外觀的因素。實際上,有一種方法通過一個調用glMaterialfv()同時設定材質環境光和散射光。下面是定義材質爲藍色的示例:

    GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);

 

正如 glColor4f(), 設置材質指示隨後所有物體繪製的方式直到另一個材質被指定。如果將上敘代碼放置於我們的繪製代碼之前,那麼運行時你將看到球體變爲藍色。由於環境光沒有散射光強,其下方只是稍暗。

 

 

有時你希望更多地控制並希望分別設定材質對環境光和散射光的反射方式。例如,下例中,材質從環境光反射藍色,從散射光反射紅色:

    GLfloat ambient[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
    GLfloat diffuse[] = {0.9, 0.0, 0.1, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);

 

運行結果:

 

在此情況下,它看上去像我們投射了有色光到球體上,實際上卻不是這樣。原因是我們反射了與環境光不一樣的定向光線。

 

大部分情況下,如果你希望產生彩色光的效果,你只需創建彩色光線然後使用GL_AMBIENT_AND_DIFFUSE來指定材質顏色。但是有時卻希望分別設置它們產生特殊效果或在不引起創建額外光線開銷的情況下假造一個分離的彩色點光源。記住:你每增加一個光源,也就增加了每秒鐘的運算量,所以有時候欺騙並不完全是一件壞事。

 

高光和光澤

 

你還可以單獨設置場景中高光元素的反射方式,從而控制高光“熱點”的亮度。一個叫 GL_SHININESS的參數與材質的高光元素一起定義了高光熱點的大小。如果你設定了材質的 GL_SPECULAR 值,你還應該定義其反光度。反光度越高,高光反射越小,所以默認值 0.0 幾乎完全淹沒了散射光,因此看上去很糟糕。

 

讓我們回到藍色球體,增加高光熱點:

    GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
    GLfloat specular[] = {0.3, 0.3, 0.3, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 25.0);

 

我們使用了一個較暗的白色作爲球體的高光值。這個值看上去有些小,但就是這麼小的值也能產生顯著的效果。光線高光元素的值與材質的高光元素相乘所產生的光集中在材質反光度指定的區域。下面是上敘代碼產生的結果:

 

 

現在,球體上有一個小的區域具有更強的反射光。我們可以通過增強光或光的高光元素,或者通過增加材質的反光度使這個點更亮。我們還可以通過調整反光度改變高光的大小。材質反光度越高,高光越集中。例如,如果我們將反光度從25.0 改爲 50.0,我們將得到一個更小的熱點,它使得球體顯得更具有光澤。

 

 

但是,有一點要小心。本文之所以改爲使用球體的部分原因以及爲什麼球體使用較高的頂點數目的原因是高光在一個低面數的物體上看上去實在糟糕。注意一下,如果我減少球體的頂點數會發生什麼:

                                                                         

 

高光使三角形邊緣突出,高光通常在我們的遊戲中經常使用的低面片的物體上表現不佳。在常規OpenGL中,有一個稱爲 着色器(shader) 的機制可以用來爲低面片物體產生較爲理想的結果,但目前iPhone上的OpenGL ES並不支持此功能(譯者注:iPhone 3GS支持OpenGL ES 2.0,有shader功能。但有一個問題就是OpenGL ES 1.1與OpenGL ES 2.0並不完全兼容)在遊戲中如果你想使低面片物體漂亮的唯一方法就是完全摒棄高光元素而使用紋理映射,這將在下一篇文章中談到。

 

自發光

 

還剩最後一個材質的重要屬性,它稱爲自發光元素。通過設定自發光元素,使得材質看上去會發射我們指定的顏色。它並不是真正在發光。例如,其周邊物體並不會被髮射的光線影響。如果你希望一個物體像燈泡一樣發光照亮其他物體,由於在OpenGL ES中只有光源會發光(聽上去像廢話),你需要將自發光元素和與物體同一位置處的實際光源結合起來。但是自發光元素可以使物體漂亮地發光。

 

例如,我們可以爲藍色球體添加綠色的光澤:

    GLfloat emission[] = {0.0, 0.4, 0.0, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);

 

結果如下:

 

自發光元素影響整個材質,因此 GL_EMISSION的值將與落入物體指定區域的任何類型的光相疊加。請注意,甚至上圖中的高光部分也成了一點藍-綠色而不是純白色。在高光點處其效果是很微小的,但在只有環境光被反射的底部效果更爲明顯。它實際影響整個物體。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章