一.實驗目的:
了解掌握OpenGL程序的光照與材質,能正確使用光源與材質函數設置所需的繪制效果。
二.實驗內容:
(1)下載並運行Nate Robin教學程序包中的lightmaterial程序,試驗不同的光照與材質系數;
(2)運行示范代碼1,了解光照與材質函數使用;
(3)在示范代碼2的基礎上,為其增加光照與材質效果,如下圖所示。
(a)原圖 (b)增加光照后的效果
三.實驗原理:
為在場景中增加光照,需要執行以下步驟:
(1) 設置一個或多個光源,設定它的有關屬性;
(2) 選擇一種光照模型;
(3) 設置物體的材料屬性。
具體如下:
1.設置光源
環境光
環境光是一種無處不在的光。環境光源放出的光線被認為來自任何方向。因此,當你僅為場景指定環境光時,所有的物體無論法向量如何,都將表現為同樣的明暗程度。
點光源
由這種光源放出的光線來自同一點,且方向輻射自四面八方。
平行光
平行光又稱鏡面光,這種光線是互相平行的。從手電筒、太陽等物體射出的光線都屬於平行光。
聚光燈
這種光源的光線從一個錐體中射出,在被照射的物體上產生聚光的效果。使用這種光源需要指定光的射出方向以及錐體的頂角α。
(2)光的成分
對於每一種光源,都有漫射光和平行光兩種成分。在OpenGL中,環境光也被作為一種特殊的光源的成分來看待。漫射光是指在光源中能夠被漫反射的光的顏色成分(白色則包含所有顏色),而平行光是指光源中所有能夠被鏡面反射的光的顏色成分。通過指定這兩種成分的顏色,就能決定光源是平行光源還是點光源。
OpenGL可以同時為我們提供8個有效的光源。也就是說,我們最多可以同時啟用8個光源。它們分別是GL_LIGHT0,GL_LIGHT1,GL_LIGHT2 ……其中,GL_LIGHT0是最特殊的一個光源。我們可以為GL_LIGHT0指定環境光成分。
a) 設置環境光
對於GL_LIGHT0,我們可以為其指定環境光成分。 調用
glLightfv(GL_LIGHT0,GL_AMBIENT, ambientLight);
來設置場景的環境光。在上述函數調用中,第一個參數表示我們要對GL_LIGHT0進行設置,第二個參數表示我們要設置的是環境光成分,第三個參數則是一個數組,它有4個值,分別表示光源中含有紅、綠、藍三種光線的成分。一般情況下都為1,最后一項為透明度值,一般也為1。完整的代碼是這樣的:
intAmbientLight[4]={1,1,1,1};
glLightfv(GL_LIGHT0,GL_AMBIENT, AmbientLight);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);
請注意在上述代碼的第三行和第四行我們分別調用了glEnable函數開啟GL_LIGHT0光源和光照系統。
b)設置漫射光成分
通過對漫射光成分的設置,我們可以產生一個點光源。方法和設置環境光成分相似,只需調用
glLightfv(GL_LIGHT0,GL_DIFFUSE, DiffuseLight);
即可。其中DiffuseLight是漫射光的顏色成分。一般情況下也為(1,1,1,1)。
c)設置鏡面光成分
通過對鏡面光成分的設置,我們可以產生一個平行光源。方法和設置漫射光成分相似,只需調用
glLightfv(GL_LIGHT0,GL_SPECULAR, SpecularLight);
即可。其中SpecularLight是漫射光的顏色成分。可以根據不同需要指定不同的顏色。
(4)設置光源的位置
對於點光源和平行光源,我們常常需要指定光源的位置來產生需要的效果。方法仍然是調用glLightfv函數,僅僅是換換參數而已:
glLightfv(GL_LIGHT0,GL_POSITION,@LightPosition);
其中,LightPosition也是一個四維數組,四維數組的前3項依次為光源位置的X,Y,Z分量,第四個值很特殊,一般為1或-1。當LightPosition[4]=-1的時候,表示光源位於距離場景無限遠的地方,無論前面設置的X,Y,Z是什么值。當LightPosition[4]=1時,光源的位置就是前三項所指定的位置。
2.光照模型
OpenGL的光照模型是用來模擬現實生活中的光照的。
3.材質設定
(1)材質顏色
OpenGL用材料對光的紅、綠、藍三原色的反射率來近似定義材料的顏色。象光源一樣,材料顏色也分成環境、漫反射和鏡面反射成分,它們決定了材料對環境光、漫反射光和鏡面反射光的反射程度。在進行光照計算時,材料對環境光的反射率與每個進入光源的環境光結合,對漫反射光的反射率與每個進入光源的漫反射光結合,對鏡面光的反射率與每個進入光源的鏡面反射光結合。對環境光與漫反射光的反射程度決定了材料的顏色,並且它們很相似。對鏡面反射光的反射率通常是白色或灰色(即對鏡面反射光中紅、綠、藍的反射率相同)。鏡面反射高光最亮的地方將變成具有光源鏡面光強度的顏色。例如一個光亮的紅色塑料球,球的大部分表現為紅色,光亮的高光將是白色的。
(2)材質定義
材質的定義與光源的定義類似。其函數為:
void glMaterial{if}[v](GLenum face,GLenum pname,TYPE param);
定義光照計算中用到的當前材質。face可以是GL_FRONT、GL_BACK、GL_FRONT_AND_BACK,它表明當前材質應該應用到物體的哪一個面上;pname說明一個特定的材質;param是材質的具體數值,若函數為向量形式,則param是一組值的指針,反之為參數值本身。非向量形式僅用於設置GL_SHINESS。pname參數值具體內容見下表。另外,參數GL_AMBIENT_AND_DIFFUSE表示可以用相同的RGB值設置環境光顏色和漫反射光顏色。
___________________________________________________________________
參數名 缺省值 說 明
GL_AMBIENT (0.2,0.2,0.2,1.0) 材料的環境光顏色
GL_DIFFUSE (0.8,0.8,0.8,1.0) 材料的漫反射光顏色
GL_AMBIENT_AND_DIFFUSE 材料的環境光和漫反射光顏色
GL_SPECULAR (0.0,0.0,0.0,1.0) 材料的鏡面反射光顏色
GL_SHINESS 0.0 鏡面指數(光亮度)
GL_EMISSION (0.0,0.0,0.0,1.0) 材料的輻射光顏色
GL_COLOR_INDEXES (0,1,1) 材料的環境光、漫反射光和鏡面光顏色
_______________________________________________
(3)材質RGB值和光源RGB值的關系
材質的顏色與光源的顏色有些不同。對於光源,R、G、B值等於R、G、B對其最大強度的百分比。若光源顏色的R、G、B值都是1.0,則是最強的白光;若值變為0.5,顏色仍為白色,但強度為原來的一半,於是表現為灰色;若R=G=1.0,B=0.0,則光源為黃色。對於材質,R、G、B值為材質對光的R、G、B成分的反射率。比如,一種材質的R=1.0,G=0.5,B=0.0,則材質反射全部的紅色成分,一半的綠色成分,不反射藍色成分。也就是說,若OpenGL的光源顏色為(LR,LG,LB),材質顏色為(MR,MG,MB),那么,在忽略所有其他反射效果的情況下,最終到達眼睛的光的顏色為(LR*MR,LG*MG,LB*MB)。同樣,如果有兩束光,相應的值分別為(R1,G1,B1)和(R2,G2,B2),則OpenGL將各個顏色成分相加,得到(R1+R2,G1+G2,B1+B2),若任一成分的和值大於1(超出了設備所能顯示的亮度)則約簡到1.0。
四.示范代碼:
(1)茶壺光照
#include <GL/glut.h>
#include <stdlib.h>
// Initialize material property, light source, lighting model, * and depth buffer.
void init(void)
{
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat Light_Model_Ambient[] = { 0.2 , 0.2 , 0.2 , 1.0 }; //
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_SMOOTH);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
glLightModelfv( GL_LIGHT_MODEL_AMBIENT , Light_Model_Ambient ); //
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
}
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glutSolidSphere (1.0, 20, 16);
glutSolidTeapot(0.5);
glFlush ();
}
void reshape (int w, int h)
{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-1.5, 1.5, -1.5*(GLfloat)h/(GLfloat)w,
1.5*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-1.5*(GLfloat)w/(GLfloat)h,
1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize (500, 500);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
(2)太陽系:
#include <GL/glut.h>
#include <stdlib.h>
static int year = 0, day = 0, moon = 0;
void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_FLAT);
}
void display(void)
{
glClear (GL_COLOR_BUFFER_BIT);
glColor3f (1.0, 1.0, 1.0);
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glutSolidSphere(1.0, 20, 16); /* draw sun */
glRotatef ((GLfloat) year, 0.0, 1.0, 0.0);
glTranslatef (2.0, 0.0, 0.0);
glRotatef ((GLfloat) day, 0.0, 1.0, 0.0);
glColor3f(0.0, 0.0, 1.0);
glutSolidSphere(0.3, 10, 8); /* draw earth */
glTranslatef (1.0, 0.0, 0.0);
glRotatef ((GLfloat) moon, 0.0, 1.0, 0.0);
glColor3f(1.0, 1.0, 1.0);
glutSolidSphere(0.2, 10, 8); /* draw moon */
glPopMatrix();
glutSwapBuffers();
}
void reshape (int w, int h)
{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt (0.0, 5.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
void keyboard (unsigned char key, int x, int y)
{
switch (key) {
case 'd':
day = (day + 10) % 360;
moon = (moon + 5) % 360;
glutPostRedisplay();
break;
case 'D':
day = (day - 10) % 360;
glutPostRedisplay();
break;
case 'y':
year = (year + 5) % 360;
day = (day + 10) % 360;
moon = (moon + 5) % 360;
glutPostRedisplay();
break;
case 'Y':
year = (year - 5) % 360;
glutPostRedisplay();
break;
case 'm':
moon = (moon + 5) % 360;
glutPostRedisplay();
break;
case 27:
exit(0);
break;
default:
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize (800, 600);
glutInitWindowPosition (100, 100);
glutCreateWindow (argv[0]);
init ();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}