《計算機圖形學》這門課的第二個實驗,覺得還挺有意思的…
實驗要求
設計一個OpenGL程序,創建一個三維迷宮,支持替身通過一定交互手段在迷宮中漫遊,基本功能包括:
- 迷宮應當至少包含10 * 10 個Cell,不能過於簡單,下圖給出一種示例。
- 讀取給定的替身模型,加載到場景中。
- 鍵盤方向鍵控制替身轉向與漫遊。
- 有碰撞檢測,替身不應當穿牆。
- 支持切換第一視角和第三視角進行觀察。
- 迷宮場景中的牆、地面等應貼上紋理。
附加要求(可選擇一個):
- 同時加入二維輔助地圖,替身在三維迷宮探索的同時,在小地圖中顯示已經探索的區域;(我選的是這一個,其他的由於最近事情比較多就沒有做,以後有機會再補吧…如果有機會的話…=。=)
- 在俯視狀態下,可以通過鼠標點選替身需要到達的目的地,通過尋徑算法,控制替身自動到達目的地;
- 迷宮地圖交互編輯功能,例如,可以設計一個二維地圖編輯器,根據用戶的繪製,拉伸得到三維迷宮場景;
- 其他相當難度,可以增加迷宮遊戲趣味性的功能(需要通過指導老師認可)
實現過程
投影模式採用透視投影,第一、三視角之間的切換隻需改變gluLookAt(GLdouble eyex,GLdouble eyey,GLdouble eyez,GLdouble centerx,GLdouble centery,GLdouble centerz,GLdouble upx,GLdouble upy,GLdouble upz);
的參數值即可。
main()
方法:
int main(int argc,char **argv){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE|GLUT_DEPTH);
glutInitWindowPosition(300, 50);
glutInitWindowSize(700, 700);
glutCreateWindow("迷宮");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
init()
方法:
void init(){
glClearColor(0.8, 0.8, 0.8, 1);//用灰色清屏
glEnable(GL_DEPTH_TEST);//開啓深度測試
glEnable(GL_TEXTURE_2D);//啓用二維紋理
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50, 1, 0.5, 100);/*其實一開始ZNear設的是1~*/
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lookX, lookY, lookZ, centerX,centerY,centerZ,upX,upY,upZ);
glShadeModel(GL_SMOOTH); //使用平滑明暗處理
glEnable(GL_DEPTH_TEST); //剔除隱藏面
glEnable(GL_CULL_FACE); //不計算多邊形背面
glFrontFace(GL_CCW); //多邊形逆時針方向爲正面
{
//三視圖光源
GLfloat light0_position[] = {10.0f, 10.0f, 0.0f, 1.0f };
GLfloat light0_ambient[] = { 0.8f, 0.8f, 0.8f, 1.0f };
GLfloat light0_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
//一視圖光源
GLfloat light1_position[] = { 10.0f, 10.0f, 0.0f, 1.0f };
GLfloat light1_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
GLfloat light1_diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
GLfloat light1_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
glLightfv(GL_LIGHT1, GL_POSITION, light1_position);
glEnable(GL_LIGHTING);//開啓光照明
glEnable(GL_DEPTH_TEST);
}
參數的初始值:
double lookX=0, lookY=0, lookZ=15;//相機在世界座標的位置
double centerX = 0, centerY = 0, centerZ = 0;//相機鏡頭對準的物體在世界座標的位置
double upX = 0, upY = 1, upZ = 0;//相機向上的方向在世界座標中的方向
關於gluPerspective()
的理解推薦看OpenGL函數思考-gluPerspective。gluLookAt()
挺好理解的就不推薦了。
display()
方法:
void display(){
drawMaze();//畫迷宮
glutSwapBuffers();/*這句很重要!!!剛開始學OpenGL的時候沒注意這個,結果畫出來什麼都沒有是全白的。開了雙緩衝就要寫。*/
}
迷宮我是用對應位置及數量的小立方體拼起來的,所以先聲明瞭一個方法drawCube(double x, double y, double z)
專門用於繪製立方體。
void drawCube(double x, double y, double z){
glPushMatrix();/*!劃重點!不寫後果很嚴重(當然這個錯誤不是我犯得哈哈)*/
glTranslatef(x, y, z);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glNormal3f(0.0f, 1.0f, 0.0f);//法向量,光照明要用的
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, -1.0f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, -1.0f);
glVertex3f(0.5f, 0.5f, -0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(-1.0f, 0.0f, 0.0f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glVertex3f(-0.5f, 0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, -0.5f);
glVertex3f(-0.5f, -0.5f, 0.5f);
glEnd();
glBegin(GL_QUADS);
glNormal3f(1.0f, 0.0f, 0.0f);
glVertex3f(0.5f, 0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, 0.5f);
glVertex3f(0.5f, -0.5f, -0.5f);
glVertex3f(0.5f, 0.5f, -0.5f);
glEnd();
glPopMatrix();/*有Push當然要有Pop*/
}
畫迷宮我是用數組存的,因爲還要實現同時加入二維輔助地圖,替身在三維迷宮探索的同時,在小地圖中顯示已經探索的區域這個功能。(感覺很弱雞啊…但是也沒想出其他好辦法…好在跑起來不卡…哈…哈…)
聲明數組int maze[900]
用於存儲一個30x30的迷宮。用0和1初始化數組。
- maze[i]=0: 座標(i - i / 30 * 30, i / 30,-20)處不繪製立方體。
- maze[j]=1: 座標(j - j / 30 * 30, j / 30,-20)處繪製立方體。
(初始化數組的過程趕時間寫的超爛…不放了就…)
void drawMaze(){
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, 700, 700);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0,700,700);
glEnable(GL_LIGHT0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(50, 1, 0.5, 100);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(lookX, lookY, lookZ, centerX, centerY, centerZ, upX, upY, upZ);
//drawGamer();
//畫三維地圖
glPushMatrix();
glTranslatef(-15, -14, 0);
for (int i = 0; i < 900; i++){
if (maze[i] == 1||maze[i]==-1){
drawCube(i - i / 30 * 30, i / 30,-20);
}
}
glPopMatrix();
//畫地板
glPushMatrix();
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glVertex3f(14, 15, -20.5);
glVertex3f(-15, 15, -20.5);
glVertex3f(-15, -14.9, -20.5);
glVertex3f(14, -14.9, -20.5);
glEnd();
glPopMatrix();
}
效果應該就是這樣orz有點醜…貼上紋理會好很多的…
有空再接着寫實現了全部功能的:)
完整源代碼下載