update: 2012/04/11
reference:
1. 原文:
iPhone Development: Procedural Spheres in OpenGL ES
iPhone Development: OpenGL ES From the Ground Up, Part 5: Living in a Material World
2. 翻譯:
從零開始學習OpenGL ES之五 – 材質
材質: OpenGL 材質
A. 說明
1. 藉由定義材質的反射光來定義 OpenGL ES 中的材質, 如果一個材質定義為反射
紅光, 那麼在正常的白光下, 它將顯示紅色.
2. 在 OpenGL 中 (至少在使用光滑著色處理和光效時), 材質是沒有顏色的. OpenGL
具有分別定義材質是怎樣反射 OpenGL 光效三要素 (環境, 散射和鏡射) 的能力.
另外, 它還具有指定材質自發光(emissive) 屬性的能力.
----------------------------------------------------------------------------------------
B. OpenGL 材質
1. 指定材質
a. 要在 OpenGL 創建一個材質, 我們通常必須多次呼叫 glMaterialf() 或者
glMaterialfv() 函數以完全定義材質. 所有未定義的元素或屬性預設值為 0,
或者以顏色來說為黑色.
b. glMaterialf() 或 glMaterialfv() 的參數說明:
例如:
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
(1). 第一個參數, 是用於指定是否材質影響多邊形的前, 後或兩者之列舉值
(GL_ENUM). 實際上除了為了與 OpenGL 兼容, 第一個參數在 OpenGL
ES 中只有一個有效的選項: GL_FRONT_AND_BACK, 它簡單地表示材質
適用於任何繪製的多邊形. 常規 OpenGL 允許你通過傳遞 GL_FRONT,
GL_BACK, 或者 GL_FRONT_AND_BACK 來為正面和背面指定不同的
材質. 但是 OpenGL ES 僅支持 GL_FRONT_AND_BACK.
(2). 第二個參數是指示正在設定材質的哪個元素或屬性之 GL_ENUM. 它們
像傳遞給 glLightfv() 的值一樣, 比如 GL_AMBIENT. (在此例為:
GL_AMBIENT_AND_DIFFUSE)
(3). 最後的參數是 GL_FLOAT 或包括了實際屬性或元素的 GL_FLOAT
資料的指標. (在此例為: ambientAndDiffuse)
c. 材質的最重要元素是環境光和散射光, 因為它們決定了材質是怎樣反射大量
光線的. 上一篇文章使用的 "繪製球體" 的程式碼定義了正如太陽光或白熾燈
產生的白色, 它具有平均分佈的各種波長和顏色的光. 如果光不是白色, 球體
看上去會有不同的外觀. 例如, 反射至紅色材質的藍光將產生紫色陰影. 簡單
起見, 我們只使用白色光. 當然, 你可以隨意改變光的顏色進行試驗看看光和
材質是怎樣交互作用的. 大部分時候, 它們在 OpenGL ES 中的表示與現實
生活中完全一樣.
--------------------------------------------------------------------------------------
2. 環境光和散射光
a. 說明:
當討論 OpenGL 的材質時, 我們需要同時討論環境光和散射光, 這是因為這兩個
元素是一起工作從而決定物體被感知的顏色的. 材質怎樣反射這兩個元素決定
了物體被感知的顏色. 大約 90% 或更多的情況下, 將材質的環境光和散射光
參數設定成一樣. 這樣做, 使它們成為決定物體陰影和外觀的因素.
b. 定義材質為藍色
(1). 開啓 ViewController.m 檔案, 修改如下:
....
- (void)drawSpheres
{
....
//@add 材質於繪製之前
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
// Draw code here ....
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleFanVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleFanNormals);
glDrawArrays(GL_TRIANGLE_FAN, 0, sphereTriangleFanVertexCount);
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleStripVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleStripNormals);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sphereTriangleStripVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
....
}
....
(2). 編譯並執行:
說明: 如同 glColor4f(), 設置材質指示隨後所有物體繪製的方式直到另一個材質
被指定. 此處由於環境光沒有散射光強,其下方只是稍暗.
c. 分別設定材質對環境光和散射光的反射方式
(1). 開啓 ViewController.m 檔案, 修改如下: 被指定. 此處由於環境光沒有散射光強,其下方只是稍暗.
c. 分別設定材質對環境光和散射光的反射方式
....
- (void)drawSpheres
{
....
//@add 材質於繪製之前
/*
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
*/
//@update 材質
//
// 材質: 從環境光反射藍色
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);
// Draw code here ....
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleFanVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleFanNormals);
glDrawArrays(GL_TRIANGLE_FAN, 0, sphereTriangleFanVertexCount);
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleStripVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleStripNormals);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sphereTriangleStripVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
....
}
....
(2). 編譯並執行:
說明: 它看上去像我們投射了有色光到球體上, 原因是我們反射了與環境光
不一樣的定向光線.
d. 大部分情況下, 如果你希望產生彩色光的效果, 你只需創建彩色光線然後使用
GL_AMBIENT_AND_DIFFUSE 來指定材質顏色. 但是有時卻希望分別設置
它們產生特殊效果或在不引起創建額外光線開銷的情況下假造一個分離的
彩色點光源. 記住: 你每增加一個光源, 也就增加了每秒鐘的運算量.
不一樣的定向光線.
d. 大部分情況下, 如果你希望產生彩色光的效果, 你只需創建彩色光線然後使用
GL_AMBIENT_AND_DIFFUSE 來指定材質顏色. 但是有時卻希望分別設置
它們產生特殊效果或在不引起創建額外光線開銷的情況下假造一個分離的
彩色點光源. 記住: 你每增加一個光源, 也就增加了每秒鐘的運算量.
----------------------------------------------------------------------------------------
C. 高光和光澤
1. 說明:
你可以單獨設置場景中鏡射元素的反射方式, 從而控制鏡射 "熱點" 的亮度.
一個稱為 GL_SHININESS 的參數與材質的鏡射元素一起定義了鏡射熱點的
大小. 如果你設定了材質的 GL_SPECULAR 值, 你還應該定義其反光度.
反光度越高, 高光反射越小, 所以預設值 0.0 幾乎完全淹沒了散射光, 因此
看上去很糟糕.
2. 增加藍色球體的鏡射熱點:
a. 開啓 ViewController.m 檔案, 修改如下: ....
- (void)drawSpheres
{
....
//@add 材質於繪製之前
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
//@update 材質
//
/*
// 材質: 從環境光反射藍色
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);
*/
//@add 增加鏡射熱點
// 使用了一個較暗的白色作為球體的鏡射值
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);
// Draw code here ....
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleFanVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleFanNormals);
glDrawArrays(GL_TRIANGLE_FAN, 0, sphereTriangleFanVertexCount);
glVertexPointer(3, GL_FLOAT, 0, sphereTriangleStripVertices);
glNormalPointer(GL_FLOAT, 0, sphereTriangleStripNormals);
glDrawArrays(GL_TRIANGLE_STRIP, 0, sphereTriangleStripVertexCount);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
....
}
....
b. 編譯並執行:
a. 說明:
現在, 球體上有一個小的區域具有更強的反射光. 我們可以通過增強光或光的
鏡射元素, 或者通過增加材質的反光度使這個點更亮. 我們還可以通過調整
反光度改變鏡射的大小. 材質反光度越高, 鏡射越集中. 例如, 如果我們將反光度
從 25.0 改為 50.0, 將得到一個更小的熱點, 它會使得球體顯得更具有光澤.
b. 開啓 ViewController.m 檔案, 修改如下:
....
- (void)drawSpheres
{
....
//@add 材質於繪製之前
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
//@update 材質
//
/*
// 材質: 從環境光反射藍色
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);
*/
//@add 增加鏡射熱點
// 使用了一個較暗的白色作為球體的鏡射值
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, 50.0);
// Draw code here ....
....
}
....
c. 編譯並執行:
備註: 鏡射會使三角形邊緣突出, 鏡射通常在遊戲中使用的低面片物體上表現不佳.
在常規 OpenGL 中, 有一個稱為 著色器(shader) 的機制可以用來為低面片
物體產生較為理想的結果, 但目前iPhone上的 OpenGL ES 並不支持此功能
(譯者註: iPhone 3GS 支持 OpenGL ES 2.0, 有 shader 功能. 但有一個問題
就是 OpenGL ES 1.1 與 OpenGL ES 2.0 並不完全兼容) 在遊戲中如果你想
使低面片物體漂亮的唯一方法就是完全摒棄鏡射元素而使用紋理映射, 這將
在之後的文章中談到.
在常規 OpenGL 中, 有一個稱為 著色器(shader) 的機制可以用來為低面片
物體產生較為理想的結果, 但目前iPhone上的 OpenGL ES 並不支持此功能
(譯者註: iPhone 3GS 支持 OpenGL ES 2.0, 有 shader 功能. 但有一個問題
就是 OpenGL ES 1.1 與 OpenGL ES 2.0 並不完全兼容) 在遊戲中如果你想
使低面片物體漂亮的唯一方法就是完全摒棄鏡射元素而使用紋理映射, 這將
在之後的文章中談到.
----------------------------------------------------------------------------------------
D. 自發光
1. 說明:
最後一個材質的重要屬性為自發光元素. 通過設定自發光元素, 使得材質看上去
會發射我們指定的顏色. 它並不是真正在發光. 例如, 其周邊物體並不會被發射的
光線影響. 如果你希望一個物體像燈泡一樣發光照亮其他物體, 由於在 OpenGL ES
中只有光源會發光, 你需要將自發光元素和與物體同一位置處的實際光源結合起來.
但是自發光元素可以使物體漂亮地發光.
2. 開啓 ViewController.m 檔案, 修改如下: ....
- (void)drawSpheres
{
....
//@add 材質於繪製之前
GLfloat ambientAndDiffuse[] = {0.0, 0.1, 0.9, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, ambientAndDiffuse);
//@update 材質
//
/*
// 材質: 從環境光反射藍色
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);
*/
//@add 增加鏡射熱點
// 使用了一個較暗的白色作為球體的鏡射值
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, 50.0);
//@add 增加綠色的自發光澤
GLfloat emission[] = {0.0, 0.4, 0.0, 1.0};
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emission);
// Draw code here ....
....
}
....
3. 編譯並執行:
備註: 自發光元素影響整個材質, 因此 GL_EMISSION 的值將與落入物體指定
區域的任何類型的光相疊加. 請注意, 甚至上圖中的鏡射部分也成了一點
藍-綠色而不是純白色. 在鏡射點處其效果是很微小的 但在只有環境光
被反射的底部效果更為明顯. 它實際影響整個物體.
區域的任何類型的光相疊加. 請注意, 甚至上圖中的鏡射部分也成了一點
藍-綠色而不是純白色. 在鏡射點處其效果是很微小的 但在只有環境光
被反射的底部效果更為明顯. 它實際影響整個物體.
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。