生活随笔
收集整理的這篇文章主要介紹了
OpenGL3D迷宫场景设计
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
最近學習用opengl庫來構建一個3D場景,以及實現場景漫游、粒子系統等效果,最終算是是做了一個3D走迷宮游戲吧。感覺最近學了好多東西,所以有必要整理整理。
一 實現效果
二 實現過程詳解
?? 1、3d場景構建
1)光照與材質
通過設置光照與材質,使得場景的顯示效果更真實。opengl加光源的方法:
[csharp]?view plaincopy
GLfloat?light_position[]?=?{0.0,?80.0,?0.0};?? ????GLfloat?light_diffuse[]?=?{1.0,?1.0,?1.0,?1.0};?? ????glLightfv(GL_LIGHT0,?GL_POSITION,?light_position);?? ????glLightfv(GL_LIGHT0,?GL_AMBIENT_AND_DIFFUSE,?light_diffuse);?? ????glEnable(GL_LIGHTING);?? ????glEnable(GL_LIGHT0);??
加一個光源至少要上面這些代碼,通過glLightfv()函數給光源設置位置以及顏色,可以加環境光G_AMBIENT、漫射光GL_DIFFUSE或鏡面光GLSPECULAR,可以同時加8個光源,上面的光源時GL_LIGHT0,其他的就是GL_LIGHT1、2等。
場景中一旦加了光源,物體就會根據自己的材質對RGB光成分反射程度而顯示不同的顏色。OpenGL給一個物體設置材質的方法
[csharp]?view plaincopy
GLfloat?diffuse[]?=?{1.0,?0.9,?0.9};?? ????glMaterialfv(GL_FRONT,?GL_AMBIENT_AND_DIFFUSE,?diffuse);??
設置材質參數后接下來畫的物體就具備了這種材質。通常使用參數GL_AMBIENT_AND_DIFFUSE給環境光和漫射光設置相同的反射程度。
2)紋理映射與多重紋理映射
給模型加紋理是為了在表面形成復雜圖案,因為設置材質只是控制表面的顯示顏色,實際上物體的表面信息要更復雜些。opengl提供了給物體貼紋理圖的方法,實際上有多種方法,但我用的是 glaux庫。
[csharp]?view plaincopy
struct?IMAGE?? {?? ????GLuint?sizeX;?? ????GLuint?sizeY;?? ????signed?char*?data;?? };?? IMAGE?*Image[3];?? GLuint?Texture[3];?? bool?loadTexture()?? {?? ????FILE*?myFile;?? ????if(!(myFile?=?fopen("wall.bmp",?"r")))?? ????return?false;?? ????Image[0]?=?(IMAGE*)auxDIBImageLoad("wall.bmp");?? ????glGenTextures(3,?&Texture[0]);?? ????glBindTexture(GL_TEXTURE_2D,?Texture[0]);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_LINEAR);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_LINEAR);?? ????glTexImage2D(GL_TEXTURE_2D,?0,?3,?Image[0]->sizeX,?Image[0]->sizeY,?0,?GL_RGB,?GL_UNSIGNED_BYTE,?Image[0]->data);?? ????if(!(myFile?=?fopen("floor.bmp",?"r")))?? ????????return?false;?? ????Image[1]?=?(IMAGE*)auxDIBImageLoad("floor.bmp");?? ????glBindTexture(GL_TEXTURE_2D,?Texture[1]);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_NEAREST);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_NEAREST);?? ????glTexImage2D(GL_TEXTURE_2D,?0,?3,?Image[1]->sizeX,?Image[1]->sizeY,?0,?GL_RGB,?GL_UNSIGNED_BYTE,?Image[1]->data);?? ????if(!(myFile?=?fopen("water.bmp",?"r")))?? ????????return?false;?? ????Image[2]?=?(IMAGE*)auxDIBImageLoad("water.bmp");?? ????glBindTexture(GL_TEXTURE_2D,?Texture[2]);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MAG_FILTER,?GL_LINEAR);?? ????glTexParameteri(GL_TEXTURE_2D,?GL_TEXTURE_MIN_FILTER,?GL_LINEAR);?? ????glTexImage2D(GL_TEXTURE_2D,?0,?3,?Image[2]->sizeX,?Image[2]->sizeY,?0,?GL_RGB,?GL_UNSIGNED_BYTE,?Image[2]->data);?? ?????? ?????? ????if(Image[0])?? ????{?? ????????if(Image[0]->data)?? ????????????free(Image[0]->data);?? ????????????free(Image[0]);?? ????}?? ????if(Image[1])?? ????{?? ????????if(Image[1]->data)?? ????????????free(Image[1]->data);?? ????????free(Image[1]);?? ????}?? ????if(Image[2])?? ????{?? ????????if(Image[2]->data)?? ????????????free(Image[2]->data);?? ????????free(Image[2]);?? ????}?? ????return?true;?? }??
上面的代碼生成了三種紋理。語句glGenTextures(3, &Texture[0])生成了三個紋理索引,存在了數組Texture中,以后每次要設置紋理信息或是想應用紋理,通過函數glBindTexture(GL_TEXTURE_2D, textureIndex)就可以取到對應的紋理,其中第二個參數就是紋理索引。
綁定紋理到物體表面:
[csharp]?view plaincopy
void?drawPolygon(GLfloat?a[3],?GLfloat?b[3],?GLfloat?c[3],?GLfloat?d[3])?? {?? ????glEnable(GL_TEXTURE_2D);?? ????glBindTexture(GL_TEXTURE_2D,?Texture[0]);?? ????glBegin(GL_POLYGON);?? ????glTexCoord2f(0.0f,?0.0f);?? ????glVertex3fv(a);?? ????glTexCoord2f(1.0f,?0.0f);?? ????glVertex3fv(b);?? ????glTexCoord2f(1.0f,?1.0f);?? ????glVertex3fv(c);?? ????glTexCoord2f(0.0f,?1.0f);?? ????glVertex3fv(d);?? ????glEnd();?? }??
多重紋理就是在物體表面貼上多個紋理的方法,要使用多重紋理需要用到另一個庫glext庫。根據下面的代碼就可以給物體表面貼上兩種紋理:
[csharp]?view plaincopy
PFNGLMULTITEXCOORD2FARBPROC?glMultiTexCoord2fARB=NULL;?? PFNGLACTIVETEXTUREARBPROC?glActiveTextureARB=NULL;?? bool?canMultiTexture?=?true;?? void?multiTextureInit()?? {?? ????glActiveTextureARB?=?(PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress("glActiveTextureARB");?? ????glMultiTexCoord2fARB?=?(PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress("glMultiTexCoord2fARB");?? ????if(glActiveTextureARB?==?NULL)?? ????????canMultiTexture?=?false;?? }?? void?multiTextureBegin()?? {?? ?????glEnable(GL_TEXTURE_2D);?? ?????glActiveTextureARB(GL_TEXTURE0_ARB);?? ?????glBindTexture(GL_TEXTURE_2D,?Texture[0]);?? ?????glActiveTextureARB(GL_TEXTURE1_ARB);?? ?????glEnable(GL_TEXTURE_2D);?? ?????glBindTexture(GL_TEXTURE_2D,?Texture[1]);?? }??
值得注意的是,多重紋理對電腦設備有要求,主要是顯示器要支持,所以一旦不支持,上面初始化的時候glActivetextureARB就會為null,這時候要做另外處理,不然后面使用這個為null的變量程序就會出錯。
3)顯示列表
顯示列表是OpenGL提供的一種方便反復調用相同的顯示函數的方法,比如你的程序中需要反復的描繪一個物體,你就最好用顯示列表來調用,這樣做能夠大大優化性能。
調用顯示列表是通過glCallList(列表索引)函數調用的,顯然沒一個顯示列表都有一個對應的索引,通過這個索引去調用顯示列表中的顯示操作。下面的代碼生成了一個畫五角星的顯示列表:
[csharp]?view plaincopy
GLuint?display_list;??
[csharp]?view plaincopy
GLuint?createDL()?? ????{?? ????????GLuint?DL;?? ????????DL?=?glGenLists(1);?? ????????glNewList(DL,GL_COMPILE);??? ????????drawFive();?? ????????glEndList();?? ????????return?DL;?? ????}??
當需要畫一個五角星的時候調用glCallList(display_list);即可。
2 場景漫游
我實現的是模擬人在迷宮中走動尋找出口的情形,通過鍵盤的上下左右鍵控制視線的改變以及位置的移動。先理解一下gluLookAt函數,我的程序里參數是這樣的gluLookAt(x, y, z, x + lx,y + ly,z + lz,0.0f,1.0f,0.0f) 總共有9個參數,前三個參數代表了照相機的位置,所以這里照相機的位置是(x,y,z),接下來三個參數是目標的中心位置,即(x+lx, y+ly,z+lz),后面三個參數一般設為0, 1, 0,表示的是照相機頭部的方向,如果把照相機看錯人眼,那照相機頭部的方向也就是我們頭的方向(所以一般向上)。因為要控制向前/后移動,所以需要知道此時視線的方向向量,實際上就是(lx, ly, lz),當改變視角是其實就是改變(lx, ly, lz)的值,所以當左右鍵事件發生時,進行以下計算:
[csharp]?view plaincopy
void?orientMe(float?ang)???? ?{???????????? ????lx?=?sin(ang);??????????? ????lz?=?-cos(ang);??????????? ????glLoadIdentity();??????????? ????gluLookAt(x,?y,?z,?x?+?lx,y?+?ly,z?+?lz,?0.0f,1.0f,0.0f);?? }??
可以注意到照相機位置還是不變的,因為只是改變了視線。當上下鍵事件發生時,改變的就是照相機的位置了。
[csharp]?view plaincopy
void?moveMeFlat(int?direction)???? {????? ????int?prev_x?=?x,?prev_z?=?z;?? ????x?=?x?+?direction*(lx)*0.1;??????????? ????z?=?z?+?direction*(lz)*0.1;???? ????glLoadIdentity();???????? ????if(isWall[(int)(x?+?93)][(int)(z?+?93)])?? ????{?? ????????x?=?prev_x;?? ????????z?=?prev_z;?? ????}?? ????gluLookAt(x,?y,?z,?x?+?lx,y?+?ly,z?+?lz,0.0f,1.0f,0.0f);?? }??
3 粒子系統的實現
粒子系統不是什么具體的東西,而是是一個很好的編程設計思想,通常用來模擬雨、雪、霧、煙花等效果。粒子系統的實現主要問題就是如何設計粒子的行為以及如何渲染粒子以達到真實的效果。我的程序里粒子系統是最后加的,跟走迷宮沒什么練習,只是覺得粒子系統挺神奇的,就試著實現各種五角星漫天飛揚的效果。這時粒子的類,定義了一個粒子的所有行為:
[csharp]?view plaincopy
?? #include<stdlib.h>?? #include<GL\glut.h>?? #include<time.h>?? #define?PI?3.1415?? class?particle?? {?? private:?? ????GLfloat?x;?? ????GLfloat?y;?? ????GLfloat?z;?? ????GLfloat?v[3];?? ????GLfloat?rotate[3];?? ????GLfloat?angle;?? ????GLfloat?color[3];?? ????GLuint?display_list;?? public:?? ????GLuint?createDL()?? ????{?? ????????GLuint?DL;?? ????????DL?=?glGenLists(1);?? ????????glNewList(DL,GL_COMPILE);??? ????????drawFive();?? ????????glEndList();?? ????????return?DL;?? ????}?? ????void?init()?? ????{?? ????????display_list?=?createDL();?? ????????angle?=?0;?? ????????y?=?rand()?%?40;?? ????????x?=?rand()?%?181?-?90;?? ????????z?=?rand()?%?181?-?90;?? ????????v[0]?=?(float)(rand()?%?8)?/?(float)10?-?0.4;?? ????????v[1]?=?(float)(rand()?%?8)?/?(float)10?-?0.4;?? ????????v[2]?=?(float)(rand()?%?8)?/?(float)10?-?0.4;?? ????????rotate[0]?=?(float)(rand()?%?7)?/?(float)7?+?5;?? ????????rotate[1]?=?(float)(rand()?%?7)?/?(float)7?+?5;?? ????????rotate[2]?=?(float)(rand()?%?7)?/?(float)7?+?5;?? ????????color[0]?=?(float)(rand()?%?5)?/?(float)5?+?0.2;?? ????????color[1]?=?(float)(rand()?%?5)?/?(float)5?+?0.2;?? ????????color[2]?=?(float)(rand()?%?5)?/?(float)5?+?0.2;?? ????}?? ????void?drawFive()?? ????{?? ????????GLfloat?out_length?=?sqrt(1.0?/?(2?-?2?*?cos(72?*?PI?/?180))),?? ????????bx?=?out_length?*?cos(18?*?PI?/?180),?? ????????by?=?out_length?*?sin(18?*?PI?/?180),?? ????????cx?=?out_length?*?sin(36?*?PI?/?180),?? ????????cy?=?-out_length?*?cos(36?*?PI?/?180);?? ????????GLfloat?fx?=?cx?*?(by?-?out_length)?/?(cy?-?out_length),?fy?=?by,?? ????????in_length?=?sqrt(fx?*?fx?+?fy?*?fy),?? ????????gx?=?in_length?*?cos(18?*?PI?/?180),?? ????????gy?=?-in_length?*?sin(18?*?PI?/?180);?? ????????GLfloat?point_a[2]?=?{0,?out_length},?? ????????point_b[2]?=?{bx,?by},?? ????????point_c[2]?=?{cx,?cy},?? ????????point_d[2]?=?{-cx,?cy},?? ????????point_e[2]?=?{-bx,?by},?? ????????point_f[2]?=?{fx,?fy},?? ????????point_g[2]?=?{gx,?gy},?? ????????point_h[2]?=?{0,?-in_length},?? ????????point_i[2]?=?{-gx,?gy},?? ????????point_j[2]?=?{-fx,?fy};?? ????????glBegin(GL_TRIANGLE_FAN);?? ????????glVertex2f(0.0f,?0.0f);?? ????????glVertex2f(point_a[0],?point_a[1]);?? ????????glVertex2f(point_f[0],?point_f[1]);?? ????????glVertex2f(point_b[0],?point_b[1]);?? ????????glVertex2f(point_g[0],?point_g[1]);?? ????????glVertex2f(point_c[0],?point_c[1]);?? ????????glVertex2f(point_h[0],?point_h[1]);?? ????????glVertex2f(point_d[0],?point_d[1]);?? ????????glVertex2f(point_i[0],?point_i[1]);?? ????????glVertex2f(point_e[0],?point_e[1]);?? ????????glVertex2f(point_j[0],?point_j[1]);?? ????????glVertex2f(point_a[0],?point_a[1]);?? ????????glEnd();?? ????}?? ????void?draw()?? ????{?? ????????GLfloat?diffuse[]?=?{color[0],?color[1],?color[2]};?? ????????glMaterialfv(GL_FRONT,?GL_AMBIENT_AND_DIFFUSE,?diffuse);?? ????????glPushMatrix();?? ????????glTranslatef(x,?y,?z);?? ????????glRotatef(angle,?rotate[0],?rotate[1],?rotate[2]);?? ????????glCallList(display_list);?? ????????glPopMatrix();?? ????}?? ????void?move(float?slowdown)?? ????{?? ????????x?+=?v[0]?/?slowdown;?? ????????y?+=?v[1]?/?slowdown;?? ????????z?+=?v[2]?/?slowdown;?? ????????angle?+=?10?/?slowdown;?? ????????if(!(x?>=?-90?&&?x?<=?90))?? ????????????die();?? ????????else?if(!(z?>=?-90?&&?z?<=?90))?? ????????????die();?? ????????else?if(!(y?>=?0?&&?y?<=?50))?? ????????????die();?? ????}?? ????void?die()?? ????{?? ????????init();?? ????}?? };??
另外也可以對比一下我實現下雨效果的粒子類:
[csharp]?view plaincopy
#include<stdlib.h>?? #include<GL\glut.h>?? #include<time.h>?? ?? class?rain?? {?? private:?? ????GLfloat?position[3];?? ????GLfloat?v0;?? ????GLfloat?g;?? ????GLfloat?size;?? ????GLfloat?sizeSet[4];?? ????GLfloat?gSet[4];?? ????GLuint?display_list;?? public:?? ????rain()?? ????{?? ????????sizeSet[0]?=?0.40;?? ????????sizeSet[1]?=?0.45;?? ????????sizeSet[2]?=?0.50;?? ????????sizeSet[3]?=?0.55;?? ????????gSet[0]?=?0.5;?? ????????gSet[1]?=?0.52;?? ????????gSet[2]?=?0.54;?? ????????gSet[3]?=?0.56;?? ????}?? ????GLuint?createDL()?? ????{?? ????????GLuint?DL;?? ????????DL?=?glGenLists(1);?? ????????glNewList(DL,GL_COMPILE);??? ????????GLUquadricObj?*qobj?=?gluNewQuadric();?? ????????gluQuadricTexture(qobj,GL_TRUE);?? ?????????gluSphere(qobj,?size,?20,?20);?? ????????glEndList();?? ????????return?DL;?? ????}?? ????void?init()?? ????{?? ????????display_list?=?createDL();?? ????????position[0]?=?rand()?%?181?-?90;?? ????????position[1]?=?50;?? ????????position[2]?=?rand()?%?181?-?90;?? ????????int?sizeIndex?=?rand()?%?4;?? ????????size?=?sizeSet[sizeIndex];?? ????????g?=?gSet[sizeIndex];?? ????????v0?=?(float)(rand()?%?6)?/?(float)20;?? ????}?? ????void?draw()?? ????{?? ????????GLfloat?diffuse[3]?=?{1.0,?1.0,?1.0};?? ????????glMaterialfv(GL_FRONT,?GL_AMBIENT_AND_DIFFUSE,?diffuse);?? ????????glPushMatrix();?? ????????glTranslatef(position[0],?position[1],?position[2]);?? ????????glCallList(display_list);?? ????????glPopMatrix();?? ????}?? ????void?move()?? ????{?? ????????position[1]?-=?v0;?? ????????v0?+=?g;?? ????????if(position[1]?<=?0)?? ????????????die();?? ????}?? ????void?die()?? ????{?? ????????init();?? ????}?? };??
雨水粒子我設計得比較簡單,初始化的時候分配它隨機一個初速度、一個初位置、加速度、大小等,每次顯示過后根據速度和加速度改變位置以實現“加速落下”的效果,還有渲染的時候需要用到雨水的紋理圖。
設計好了粒子類之后,就可以再寫一個類實現對粒子數的控制,以及對所有粒子進行初始化和顯示。
?反復調用的實現
通過理解粒子系統,我知道它是反復地調用顯示所有粒子的函數,因為每次粒子的位置都會改變,所以就形成了粒子的運動。那怎么反復調用顯示函數呢?看一下glut庫里的函數(當然如果用windows庫的話也能實現反復調用,這里只是glut庫的):
glutDisplayFunc(renderScene);每次窗口重繪時指定調用函數
glutReshapeFunc(changeSize); ? ? 每次窗口大小改變時制定調用函數
一開始想通過這兩個函數想反復調用renderScene函數,但是沒辦法,它們指定的函數只能在特定情況下被調用;
然后我就找到了glutIdleFunc(renderScene)函數,作用是設置全局的默認調用函數。當函數glutMainLoop()進行了無限等待時間循環時,如果沒有窗口事件發生,就默認調用glutIdelFunc指定的函數,這樣就可以反復調用renderScene函數了。
五角星粒子系統效果:
雪粒子效果
總結
以上是生活随笔為你收集整理的OpenGL3D迷宫场景设计的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。