OpenGL光照設置
1.設置光源
(1)光源的種類
環境光
環境光是一種無處不在的光。環境光源放出的光線被認為來自任何方向。因此,當你僅為場景指定環境光時,所有的物體無論法向量如何,都將表現為同樣的明暗程度。
點光源
由這種光源放出的光線來自同一點,且方向輻射向四面八方。
平行光
平行光又稱鏡面光,這種光線是互相平行的。從手電筒、太陽等物體射出的光線都屬於平行光。
聚光燈
這種光源的光線從一個錐體中射出,在被照射的物體上產生聚光的效果。使用這種光源需要指定光的射出方向以及錐體的頂角α。
(2)光的成分
對於每一種光源,都有漫射光和平行光兩種成分。
在OpenGL中,環境光也被作為一種特殊的光源的成分來看待。
漫射光是指在光源中能夠被漫反射的光的顏色成分(白色則包含所有顏色),
而平行光是指光源中所有能夠被鏡面反射的光的顏色成分。
通過指定這兩種成分的顏色,就能決定光源是平行光源還是點光源。
(3)設置光源成分
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。
完整的代碼是這樣的:
int AmbientLight[4]={1,1,1,1};
glLightfv(GL_LIGHT0, GL_AMBIENT,
AmbientLight);
glEnable(GL_LIGHT0); //允許0#燈使用
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說明一個特定的材質;
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) 材料的環境光、漫反射光和鏡面光顏色
param是材質的具體數值,若函數為向量形式,則param是一組值的指針,反之為參數值本身。非向量形式僅用於設置GL_SHINESS。
_______________________________________________
(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。
3.示例代碼
//繪制茶壺 #include "stdafx.h" #include <GL/glut.h> #include <stdlib.h> //自定義初始化opengl函數 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 }; //燈位置(1,1,1), 最後1-開關 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); //打開0#燈 glEnable(GL_DEPTH_TEST); //打開深度測試 } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glutSolidTeapot(0.5); /* glBegin(GL_QUADS); glVertex3f(0, 0, 10); glVertex3f(0, 0, 10); glVertex3f(20, 5, 10); glVertex3f(30, 40, -10); glEnd(); */ glFlush(); //glSwapBuffers(); } 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("茶壺"); init(); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
修改鏡面反射參數、環境光參數、燈的位置和背景色後:
//繪制太陽系 #include "stdafx.h" #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_SMOOTH); } 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; }
開始時效果:
動畫效果:
加入光照條件:
//材質反光性設置 GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; //鏡面反射參數 GLfloat mat_shininess[] = { 50.0 }; //高光指數 GLfloat light_position[] = { 3.0, 3.0, 3.0, 0.0 }; GLfloat white_light[] = { 1.0, 1.0, 1.0, 1.0 }; //燈位置(1,1,1), 最後1-開關 GLfloat Light_Model_Ambient[] = { 0.8 , 0.2 , 0.2 , 1.0 }; //環境光參數
//材質屬性 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); //打開0#燈 glEnable(GL_DEPTH_TEST); //打開深度測試
註意:記得要在glClear後加一個深度測試
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
加入光照後的效果如下
剛開始:
動畫效果:
總結
此次學習了OpenGL的光照設置後,讓我對二維到三維產生了新的認識,同時也愈發感到有趣。記得前段時間我和女友聊起光照,她說她當時畫立體畫的時候也要故意把一個點畫的特別亮,另外一些地方要進行暗處理,這樣才能顯示出立體的效果。聊天期間還給我介紹了一些專業術語...看來圖形學和繪畫,總是存在著一些異曲同工之妙的!相信後面的劇情發展會越來越精彩的。
OpenGL光照設置