還是零分 散播福音的祭司
註冊時間: 2007-09-19 文章: 164
653.83 果凍幣
|
發表於: 2008-7-2, PM 3:02 星期三 文章主題: OpenGL入門教學(05) |
|
|
這篇的新內容是光線的模擬
想要讓OpenGL打光的話,必須設定光源的位置、光源的屬性
設定物質反光的材質屬性、法向量
光源的屬性以及材質屬性都是在設定3種光:環境光(Ambient Light)、散射光(Diffuse Light)、反射光(Specular Light)
環境光:顧名思義是從四面八方照來的光,是不管方向的
散射光:就現實中而言是不光滑表面的反射光,物體有照到光的部分都有散射光散射
反射光:反射光特別亮的話則會顯的很有光澤
物體表現出來的明暗是光源屬性3種光和材質屬性3種光相乘的結果
兩者都是介於0到1的數,相乘後還是介於0到1
//------glNormal3f( x , y , z );
//
//用來設定法向量,需要這項資料才能正確打光
//可以一個面設一個向量或一個點設一個向量
//為了打光更自然還出現過許多奇怪的方法,這裡不贅述
//------glPolygonMode ( 想要顯示的面 , 顯示的模式 );
//
//第一個參數可以填:
//GL_FRONT_AND_BACK
//GL_FRONT
//GL_BACK
//第二個參數可以填:
//GL_FILL
//GL_LINE
//GL_POINT
//若沒設定這一項的話,OpenGL預設是( GL_FRONT_AND_BACK , GL_FILL )
//設定面的時候,看起來以逆時針順序設定點的那一面為正面
//------gluPerspective( 視角, rate , 最近邊界 , 最遠邊界 );
//
//第一個參數是觀察者的視角,若視角給太大的話,視野會變很廣,像是透過魚眼看世界一樣
//第二個參數是畫面寬除以高的比值,只要使用視窗的長寬來計算,投影畫面就不會變形了(跟之前的範例不同)
//最近邊界是視點到最近顯示邊界的距離,通常會給個1或0.1,但就是不可以填0
//最遠邊界設定太小或太大都不好,看情況使用
//------glLightfv( 要使用的光源 , 光源屬性 , RGBA );
//
//第一個參數是指定要改變光源屬性的光源
//在Windows下有八個光源可用,分別是GL_LIGHT0~GL_LIGHT7,省著點用啊,數量有限
//第二個參數指定要修改的是3種光的哪一種
//第三個參數放入一個陣列的指標(就是陣列的名字)
//陣列中有4個數,代表RGBA顏色值
//這種把資料寫在一個結構中,再把結構的指標當參數輸入的做法
//在API中很常見
//------glMaterialfv( 面 , 材質屬性 , RGBA );
//
//第一個參數決定現在要設定的是正面還是反面(還是正反都要)
//第一個參數可以填:
//GL_FRONT_AND_BACK
//GL_FRONT
//GL_BACK
//後面的兩個參數與glLightfv()的使用無異
代碼: |
//-----------------------------------------------------------------------------
// 2008/7/1
// 光線模擬
// by還是零分
//-----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <GL\glut.h>
int WinNumber=NULL; //用來放置視窗代碼
int old_rot_x = 0; //剛按下滑鼠時的視窗座標
int old_rot_y = 0;
int rot_x = 0; //拖曳後的相對座標,用這決定要旋轉幾度
int rot_y = 0;
int record_x = 0; //紀錄上一次旋轉的角度
int record_y = 0;
float distance = 0; //在平移矩陣(glTranslatef();)中使用
float light_position[] = { -20, 20, 0}; //光源的位置
void WindowSize(int , int ); //負責視窗及繪圖內容的比例
void Keyboard(unsigned char ,int ,int ); //獲取鍵盤輸入
void Mouse(int ,int ,int ,int ); //獲取滑鼠按下和放開時的訊息
void MotionMouse(int ,int ); //獲取滑鼠按下期間的訊息
void Display(void); //描繪
void SetLightSource(void); //設定光源屬性
void SetMaterial(void); //設定材質屬性
int main()
{
printf( "按w和s鍵調整遠近\n用Esc鍵來關閉程式\n" );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB );
glutInitWindowSize( 400, 400); //視窗長寬
glutInitWindowPosition( 600, 80); //視窗左上角的位置
WinNumber = glutCreateWindow( "這裡是視窗標題" ); //建立視窗
//下面五個是用來指定Callback函數
glutReshapeFunc ( WindowSize );
glutKeyboardFunc( Keyboard );
glutMouseFunc ( Mouse );
glutMotionFunc ( MotionMouse );
glutDisplayFunc ( Display );
SetLightSource();
SetMaterial();
glutMainLoop();
return 0;
}
void Display(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0); //用白色塗背景
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glPolygonMode (GL_BACK, GL_LINE); //設定面的背面用線條顯示
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt( 0, 0, 30.0, 0, 0, 0, 0, 1, 0); //視線的座標及方向
glTranslatef( 0, 0, distance); //沿著z軸平移
glRotatef( (float)rot_y + (float)record_y, 1.0, 0.0, 0.0); //以x軸當旋轉軸
glRotatef( (float)rot_x + (float)record_x, 0.0, 1.0, 0.0); //以y軸當旋轉軸
//畫一個沒有底面的box
glBegin(GL_QUADS);
//正面
glNormal3f(0,0,1); //設定法向量
glVertex3f(-7, 7, 7);
glVertex3f(-7,-7, 7);
glVertex3f( 7,-7, 7);
glVertex3f( 7, 7, 7);
//背面
glNormal3f(0,0,-1);
glVertex3f(-7, 7,-7);
glVertex3f( 7, 7,-7);
glVertex3f( 7,-7,-7);
glVertex3f(-7,-7,-7);
//右側面
glNormal3f(1,0,0);
glVertex3f( 7, 7, 7);
glVertex3f( 7,-7, 7);
glVertex3f( 7,-7,-7);
glVertex3f( 7, 7,-7);
//左側面
glNormal3f(-1,0,0);
glVertex3f(-7, 7,-7);
glVertex3f(-7,-7,-7);
glVertex3f(-7,-7, 7);
glVertex3f(-7, 7, 7);
//上面
glNormal3f(0,1,0);
glVertex3f(-7, 7,-7);
glVertex3f(-7, 7, 7);
glVertex3f( 7, 7, 7);
glVertex3f( 7, 7,-7);
glEnd();
glutSwapBuffers();
}
void Keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'w':
distance+=1;
break;
case 's':
distance-=1;
break;
case 27:
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glutDestroyWindow(WinNumber);
exit(0);
break;
}
glutPostRedisplay(); //令視窗重繪
}
void WindowSize(int w, int h)
{
float rate;
if( h==0 ) h = 1; //阻止h為零,分母可不能為零啊
glViewport( 0, 0, w, h); //當視窗長寬改變時,畫面也跟著變
rate = (float)w/(float)h; //畫面視野變了,但內容不變形
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective( 45, rate, 1.0, 500.0); //透視投影
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void Mouse(int button, int state, int x, int y)
{
if(state)
{
record_x += x - old_rot_x;
record_y += y - old_rot_y;
rot_x = 0; //沒有歸零會有不理想的結果
rot_y = 0;
}
else
{
old_rot_x = x;
old_rot_y = y;
}
}
void MotionMouse(int x, int y)
{
rot_x = x - old_rot_x;
rot_y = y - old_rot_y;
glutPostRedisplay();
}
void SetLightSource()
{
float light_ambient[] = { 1.0, 1.0, 1.0, 1.0};
float light_diffuse[] = { 1.0, 1.0, 1.0, 1.0};
float light_specular[] = { 1.0, 1.0, 1.0, 1.0};
glEnable(GL_LIGHTING); //開燈
// 設定發光體的光源的特性
glLightfv( GL_LIGHT0, GL_AMBIENT, light_ambient); //環境光(Ambient Light)
glLightfv( GL_LIGHT0, GL_DIFFUSE, light_diffuse); //散射光(Diffuse Light)
glLightfv( GL_LIGHT0, GL_SPECULAR,light_specular); //反射光(Specular Light)
glLightfv( GL_LIGHT0, GL_POSITION,light_position); //光的座標
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST); //啟動深度測試
}
void SetMaterial()
{
float material_ambient[] = { 0.2, 0.2, 0.2, 1.0};
float material_diffuse[] = { 0.3, 0.3, 0.3, 1.0};
float material_specular[] = { 0.2, 0.2, 0.2, 1.0};
glMaterialfv( GL_FRONT, GL_AMBIENT, material_ambient);
glMaterialfv( GL_FRONT, GL_DIFFUSE, material_diffuse);
glMaterialfv( GL_FRONT, GL_SPECULAR, material_specular);
}
|
除了加上光線模擬之外
我把正交投影矩陣換成透視投影了,用'w'和's'鍵把鏡頭拉近拉遠時,物體會變大變小
並且啟動深度測試了,終於比較像現實世界了!
關閉視窗可以用Esc鍵了,這在全螢幕模式下是必備的
拉扯視窗也不會讓圖形跟著變形了 |
|