這一章將在你的程序中加入燈光,使場景看起來和真實場景一樣。
燈光
opengl中燈光分為好幾種,都可以加入到你的場景中。
Ambiend Light 環境光
環境光沒有確切的來源方向,當環境光照射到物體時,光被反射到各個方向。
Diffuse Light 漫射光
漫射光不同于環境光,它來自某個方向,但和環境光一樣,照射到物體時,會被反射到各個方向。
Specular Light 鏡面光
鏡面光和漫射光一樣是有方向的,但它反射方向是一定的,而不像漫射光一樣反射到各個方向。所以當鏡面光照射到物體時,你會看到物體表面被照射的亮點。
Emissive Light 發射光
它來自于某一物體,該物體散發出大量的光,但不會被任何物體面反射。
為了更好的理解這幾種光,我從網絡上摘抄了一段定義:
* 環境光——經過多次反射而來的光稱為環境光,無法確定其最初的方向,但當特定的光源關閉后,它們將消失.
* 全局環境光——它們并非來自特定的光源,這些光經過了多次散射,已經無法確定其光源位于何處.
* 散射光——來自同一方向,照射到物體表面后,將沿各個方向均勻反射,因此,無論從哪個方向觀察,表面的亮度都相同.
* 鏡面反射光——來自特定方向,也被反射到特定方向.鏡面反射度與之相關.
* 材質發射光——用于模擬發光物體.在OpenGL光照模型中,表面的發射光增加了物體的亮度,它不受光源的影響,另外,發射光不會給整個場景中增加光線.
材質
你不光可以設置光的屬性,而且還可以指定不同的面對光照作出的反應,這就要指定材質屬性。
這就指定了一個面對光源反射多少。
法線
法線是一個向量垂直于(90度)某一特定面,就稱這個向量是某個面的法線。法線可以用于光的計算。如果你想讓畫出的物體對光源產生影響,那么必須指定物體每個面的法線。下面將會說明。
另一個需要注意的一點是,法線要單位化,我們不會深入數學計算,這不是我們這章的目的。如果需要會在將來某章中講解。簡明的說,一個向量的長度等于各個向量分量的平方和的平方根,再把每個向量的分量除以這個值。現在不需要過多擔心這個。
程序代碼:
下面定義兩個顏色數組,一個用于環境光,一個用于漫射光,它們是光源的顏色值。
float lightAmbient[] = { 0.2f, 0.3f, 0.6f, 1.0f };
float lightDiffuse[] = { 0.2f, 0.3f, 0.6f, 1.0f };
下面創建一個材質屬性數組,分別用于環境光和漫射光。
用材質屬性值乘以光源值得出面的反射顏色值,下面的值將會導致面反射的光失去接收光的百分之四十。每個值表示特定顏色被反射的數量。
float matAmbient[] = { 0.6f, 0.6f, 0.6f, 1.0f };
float matDiffuse[] = { 0.6f, 0.6f, 0.6f, 1.0f };
void init()
{
首先先啟用光源,這樣光才會在場景中起作用。
glEnable(GL_LIGHTING);
opengl最多允許8個光源,要使用某個光源,需要使用glEnable打開它,光源的編號是GL_LIGHTX,X的值是0---7。
指定材質屬性,可以使用glMaterialfv和glMaterialf ,glMaterialfv接受向量數組,而glMaterialf只接受一個向量。第一個參數指定那個面被更新,在opengl es中只可以使用GL_FRONT_AND_BACK,其他參數不起作用。之所以存在這個參數,是因為opengl可以設置多個參數。
第二個參數指定光源的類型,GL_AMBIENT, GL_DIFFUSE, GL_SPECULAR, GL_EMISSION 和 GL_AMBIENT_AND_DIFFUSE.
最后一個參數指定一個數組或單個值,取決于你使用的哪個函數。
下一行設置面的材質屬性:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmbient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiffuse);
燈光的設置和材質的設置相同,使用glLightfv或glLightf函數:
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
init函數沒有發生改變:
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClearDepthf(1.0f);
glVertexPointer(3, GL_FLOAT, 0, box);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_CULL_FACE);
glShadeModel(GL_SMOOTH);
}
display函數的開頭部分沒有發生改變:
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAtf(
0.0f, 0.0f, 3.0f,
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f);
glRotatef(xrot, 1.0f, 0.0f, 0.0f);
glRotatef(yrot, 0.0f, 1.0f, 0.0f);
前面我們討論了法線,法線是垂直于面的,所以前平面的法線是(0, 0, 1),后平面的法線是(0, 0, -1),兩個法線的長度為1,所以不用再單位化。
法線由glNormal3f 函數指定,并在渲染時調用。這個函數由3個float類型的數據組成單位化的向量。
// FRONT AND BACK
glColor4f(1.0f, 0.0f, 0.0f, 1.0f);
glNormal3f(0.0f, 0.0f, 1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glNormal3f(0.0f, 0.0f, -1.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
其他頁面設置也同上,
// LEFT AND RIGHT
glColor4f(0.0f, 1.0f, 0.0f, 1.0f);
glNormal3f(-1.0f, 0.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
glNormal3f(1.0f, 0.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
// TOP AND BOTTOM
glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
glNormal3f(0.0f, 1.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
glNormal3f(0.0f, -1.0f, 0.0f);
glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
glFlush();
glutSwapBuffers();
}
最后菜單增加一項彩色材質,這項選擇打開或關閉色彩跟蹤。色彩跟蹤根據當前面的色彩反色不同色的光。
case 2 :
if (glIsEnabled(GL_COLOR_MATERIAL))
glDisable(GL_COLOR_MATERIAL);
else
glEnable(GL_COLOR_MATERIAL);
break;
下面兩張圖是程序的運行結果,分別是普通燈光和色彩追蹤的效果。
普通燈光 色彩跟蹤


現在學會了在場景中添加燈光,它提供了靈活的設置,使得場景更加真實。