- 實現任務目標:
- 使用紋理貼圖,增強可視效果
- 應用座標變換,實現場景中不同物體重建
- 採用雙緩衝技術,實現場景實時繪製
- 具有一定的滑鼠、鍵盤互動功能
- 先放效果
滑鼠的互動功能有:右鍵暫停轉動,左鍵繼續轉動,滾輪向前放大,向後縮小
- IDE:opengl實現需要庫函式。用的編譯環境是visual studio。附上一個很好的教程【1】:在vs2017下配置opengl。(vs2019也可以用)
- 一個很好的入門教程【2】:OpenGL入門教程(精)。講得很仔細,通俗易懂。前幾課用到的庫都沒有超過glut的範圍。
- 事實上,對於opengl的實現主要是對於各種庫函式的呼叫,所以對於各種庫函式的認知很重要。這裡也給出一個很好的教程【3】:OpenGL庫函式彙總。
- ok,在看了上面的教程以後肯定對於opengl有了一定認識,尤其是第二個教程中講解得非常仔細。所以本文接下來的內容是建立在對那個教程的學習基礎之上,對一些我在實踐中遇到的問題作出補充。
- 下面就進入正文。
- 所包含的標頭檔案目錄
1 #include <GL/glut.h> 2 #include <stdlib.h> 3 #include <stdio.h>
- 最基本的功能是當然是建立自己的圖形並顯示出來,如上圖我建立的是日地月系統。需要的函式為display()和main()。
- 這其中很重要的一個知識點就是影像的檢視變換/模型變換、投影變換和視口變換。有關這塊的內容個人覺得教程【2】中講得不夠清楚,可以參考一些別的教程。比如:OpenGL(六) gluLookAt和gluPerspective函式解析;Opengl---gluLookAt函式詳解。
- 這裡要介紹一下opengl中的座標軸。x軸水平向右為正,y軸豎直向上為正,z軸垂直螢幕向外為正。符合右手定則。
1 void display(void) 2 { 3 glEnable(GL_DEPTH_TEST); //3、5行程式碼中跟DEPTH有關的函式是為了在同一個視窗內建立多個影像而不會被後建立的影像覆蓋。 4 glClearColor(0, 0, 0, 1); //設定“空”色。之前看到一個很好的解釋,白紙是白色的,所以白紙上的“空”色為白色。那麼信封上的“空”色就是信封的顏色。 5 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //COLOR的那個引數是清除顏色快取,設為“空”色 6 7 glMatrixMode(GL_PROJECTION); //投影變換 8 glLoadIdentity(); 9 gluPerspective(60.0, 1, 1.0, 100.0); 10 11 glMatrixMode(GL_MODELVIEW); //檢視變換/模型變換 12 glLoadIdentity(); //載入單位矩陣 13 gluLookAt(0.0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0); 15 //太陽 16 glColor3f(1.0, 0, 0); 17 glutSolidSphere(6, 20, 20); 18 //地球 19 glColor3f(0.0, 0, 1.0); 20 glTranslatef(-20.0, 0, 0); //偏移矩陣 21 glutSolidSphere(3, 20, 20); 22 //月球 23 glColor3f(1.0, 1.0, 0); 24 glTranslatef(-6.0, 0, 0); //這裡的偏移量是在上面已經偏移的基礎上再進行偏移 25 glutSolidSphere(1, 20, 20); 26 27 glutSwapBuffers(); //雙緩衝函式用到,相關內容看上面的教程【2】裡 28 }
- main()中的函式就不具體解釋了,應該都懂
1 int main(int argc, char** argv) 2 { 3 glutInit(&argc, argv); 4 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); 5 glutInitWindowSize(500, 500); 6 glutInitWindowPosition(100, 100); 7 glutCreateWindow("name"); 8 glutDisplayFunc(&display); 9 glutMainLoop(); 10 return 0; 11 }
- 現在在現有程式的基礎上加入動畫需要4步
- 1 加入全域性變數
1 static GLfloat angle = 0.0f;
- 2 在display()裡面加入旋轉的函式。由於效果是讓整個畫面都轉,這句話我選擇加在gluLookAt()後面。需要加入的語句已標紅。
1 void display(void) 2 { 3 …… …… 4 glMatrixMode(GL_MODELVIEW); 5 glLoadIdentity(); //載入單位矩陣 6 gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0); 7 glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋轉,改變的是x軸分量 8 9 glColor3f(1.0, 0, 0); 10 …… …… 11 }
- 3 編寫myIdle()函式
1 void myIdle(void) 2 { 3 angle += 1.8f; 4 if (angle >= 360.0f) 5 angle = 0.0f; 6 display(); 7 }
- 4 在主函式加入glutIdleFunc(&myIdle);可以加在剛剛的display語句下面。
1 int main(int argc, char** argv) 2 { 3 …… …… 4 glutDisplayFunc(&display); 5 glutIdleFunc(&myIdle); 6 …… …… 7 glutMainLoop(); 8 return 0; 9 }
- ok。接下來就要為我們的程式加上紋理了。首先在網上找了兩張星空的網圖。而且,為了方便起見,我把它們的格式改成了24位色的bmp圖片,尺寸為258*258。
- 至於怎麼改格式:1 24位色可以對圖片另存為時在下拉選單裡選擇。2 修改尺寸可以用win自帶的圖片編輯器。
- 我的兩張照片分別命名為“wall.bmp”,"ground.bmp"。放在源程式的同一個子目錄裡面
- 有關紋理貼圖的詳細內容繼續參考教程【2】。這裡附上我寫的程式和說明。
- 一共分為3步。
- 1 搭建矩形框架【對我的程式來說相當於有一個支架,然後把按照點對點的方式紋理圖貼上去】
- 在這一步中先只寫上矩形各個點的座標,為後面建立矩形做準備。
1 //全域性變數 2 static const GLfloat vertex_list[][3] = { 3 - 15.0f, -20.0f, -10.0f, //事實上6、7兩個點是用不到的,作為完整性就一起寫了。貼圖只在背面和底面貼了圖,為了更好的演示效果。 4 40.0f, -20.0f, -10.0f, 5 40.0f, 20.0f, -10.0f, 6 -15.0f, 20.0f, -10.0f, 7 -15.0f, -20.0f, 10.0f, 8 40.0f, -20.0f, 10.0f, 9 -15.0f, 20.0f, 10.0f, 10 40.0f, 20.0f, 10.0f, 11 };
- 2 將紋理圖讀入。寫了一個讀檔案的函式,還是參考之前的教程【2】,不多作解釋了。以及一個參考教程:OpenGL(十二) 紋理對映(貼圖)
1 //全域性變數 2 #define BMP_Header_Length 54 3 //函式 4 // 函式power_of_two用於判斷一個整數是不是2的整數次冪 5 int power_of_two(int n) 6 { 7 if (n <= 0) 8 return 0; 9 return (n & (n - 1)) == 0; 10 } 11 /* 函式load_texture 12 * 讀取一個BMP檔案作為紋理 13 * 如果失敗,返回0,如果成功,返回紋理編號 14 */ 15 GLuint load_texture(const char* file_name) 16 { 17 GLint width, height, total_bytes; 18 GLubyte* pixels = 0; 19 GLuint last_texture_ID = 0, texture_ID = 0; 20 21 // 開啟檔案,如果失敗,返回 22 FILE* pFile; 23 errno_t err; 24 err = fopen_s(&pFile, file_name, "rb"); //在vs中使用fopen_s()函式的示例。 25 if (!pFile) exit(0); 26 27 // 讀取檔案中圖象的寬度和高度 28 fseek(pFile, 0x0012, SEEK_SET); 29 fread(&width, sizeof(width), 1, pFile); 30 fread(&height, sizeof(height), 1, pFile); 31 fseek(pFile, BMP_Header_Length, SEEK_SET); 32 33 // 計算每行畫素所佔位元組數,並根據此資料計算總畫素位元組數 34 { 35 GLint line_bytes = width * 3; 36 while (line_bytes % 4 != 0) 37 ++line_bytes; 38 total_bytes = line_bytes * height; 39 } 40 41 // 根據總畫素位元組數分配記憶體 42 pixels = (GLubyte*)malloc(total_bytes); 43 if (pixels == 0) 44 { 45 fclose(pFile); 46 return 0; 47 } 48 49 // 讀取畫素資料 50 if (fread(pixels, total_bytes, 1, pFile) <= 0) 51 { 52 free(pixels); 53 fclose(pFile); 54 return 0; 55 } 56 57 // 對就舊版本的相容,如果圖象的寬度和高度不是的整數次方,則需要進行縮放 58 // 若影像寬高超過了OpenGL規定的最大值,也縮放 59 { 60 GLint max; 61 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); 62 if (!power_of_two(width) 63 || !power_of_two(height) 64 || width > max 65 || height > max) 66 { 67 const GLint new_width = 256; 68 const GLint new_height = 256; // 規定縮放後新的大小為邊長的正方形 69 GLint new_line_bytes, new_total_bytes; 70 GLubyte* new_pixels = 0; 71 72 // 計算每行需要的位元組數和總位元組數 73 new_line_bytes = new_width * 3; 74 while (new_line_bytes % 4 != 0) 75 ++new_line_bytes; 76 new_total_bytes = new_line_bytes * new_height; 77 78 // 分配記憶體 79 new_pixels = (GLubyte*)malloc(new_total_bytes); 80 if (new_pixels == 0) 81 { 82 free(pixels); 83 fclose(pFile); 84 return 0; 85 } 86 87 // 進行畫素縮放 88 gluScaleImage(GL_RGB, 89 width, height, GL_UNSIGNED_BYTE, pixels, 90 new_width, new_height, GL_UNSIGNED_BYTE, new_pixels); 91 92 // 釋放原來的畫素資料,把pixels指向新的畫素資料,並重新設定width和height 93 free(pixels); 94 pixels = new_pixels; 95 width = new_width; 96 height = new_height; 97 } 98 } 99 100 // 分配一個新的紋理編號 101 glGenTextures(1, &texture_ID); 102 if (texture_ID == 0) 103 { 104 free(pixels); 105 fclose(pFile); 106 return 0; 107 } 108 109 // 繫結新的紋理,載入紋理並設定紋理引數 110 // 在繫結前,先獲得原來繫結的紋理編號,以便在最後進行恢復 111 GLint lastTextureID = last_texture_ID; 112 glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID); 113 glBindTexture(GL_TEXTURE_2D, texture_ID); 114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 118 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 119 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, 120 GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); 121 glBindTexture(GL_TEXTURE_2D, lastTextureID); //恢復之前的紋理繫結 122 free(pixels); 123 return texture_ID; 124 }
- 3 在display()中開啟狀態機->讀取紋理圖片->搭起矩形框架->貼圖->關閉狀態機。
- 這裡踩過的坑就是關於狀態機的開閉問題。如果沒有關閉狀態機,顯示的影像中之前畫的幾個球都是全黑的。這是因為紋理貼圖會干擾別的顏色。
- 其中用到的glTexCoord2f()函式可以參考百度的這個示例。
1 //全域性變數 2 GLuint texGround; 3 GLuint texWall; 4 //函式補充 5 void display(void) 6 { 7 …… ……//之前內容的後面加入一下內容 8 glEnable(GL_TEXTURE_2D); //開啟狀態機 9 texGround = load_texture("ground.bmp"); 10 texWall = load_texture("wall.bmp"); 11 12 //繪製底面 13 glBindTexture(GL_TEXTURE_2D, texGround); 14 glBegin(GL_QUADS); 15 glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[4]); //點對點 16 glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[5]); 17 glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[1]); 18 glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[0]); 19 glEnd(); 20 //繪製立面 21 glBindTexture(GL_TEXTURE_2D, texWall); 22 glBegin(GL_QUADS); 23 glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[0]); 24 glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[1]); 25 glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[2]); 26 glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[3]); 27 glEnd(); 28 glDisable(GL_TEXTURE_2D);//關閉狀態機 29 glutSwapBuffers(); 30 }
- ok。那麼到這裡我們已經完成了紋理貼圖、雙緩衝繪製和場景重建的任務啦。接下來還有滑鼠互動的任務。那麼在這裡先插入一個新的函式講解:reshape()。
- 關於reshape()的原理呢可以去查查資料。我說說我的理解吧。簡單來說呢就是在你顯示視窗時,如果你拉動邊框,視窗內的影像不會隨著你拉動而改變。
- 附上一個簡單的圖片示例。
可以看到在右邊的圖中,我拉動了視窗的邊框,則影像的形狀也改變了。
- reshape()就能在窗體大小被改變時,視窗大小不變,影像比例也不變。
- 那麼同樣的,完成這個功能需要2步。
- 1 寫一個reshape()函式
1 void reshape(int w, int h) 2 { 3 glViewport(0, 0, 500, 500); 4 glMatrixMode(GL_PROJECTION); 5 glLoadIdentity(); 6 gluPerspective(60.0, 1, 1, 100.0); 7 glMatrixMode(GL_MODELVIEW); 8 glLoadIdentity(); 9 gluLookAt(0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0); 10 }
- 2 在main函式中加入一句
1 int main(int argc, char** argv) 2 { 3 …… …… 4 glutDisplayFunc(&display); 5 glutReshapeFunc(&reshape); 6 glutIdleFunc(&myIdle); 7 …… …… 8 }
- ok。最後的最後,要完成滑鼠的互動了。
- 我所設定的滑鼠的功能包括:右鍵暫停、左鍵繼續;滾輪向上放大,滾輪向下縮小。
- 前兩個改變的是轉過的角度angle,後兩個則跟我們所建立的檢視模型,也就是之前用過glLookAt()函式的引數有關。
- 對於滑鼠互動用到的函式及參量是void myMouse(int button, int state, int x, int y);關於這個更多的資訊可以自行查詢。
- 那麼同樣的,完成這個需要2 / 3步。但是我分為兩個部分來講。首先是對於右鍵暫停和左鍵繼續的部分。
- 1 之前的顯示函式裡已經有了一個angle變數用來控制角度,所以我們要做的就是停掉這個angle變數的自增,所以我們要停用myIdle函式。
1 void myMouse(int button, int state, int x, int y) 2 { 3 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 4 { 5 glutIdleFunc(&myIdle); 6 } 7 if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) 8 { 9 glutIdleFunc(NULL); 10 } 11 }
- 2 在主函式中加入語句。
1 int main(int argc, char** argv) 2 { 3 …… …… 4 glutCreateWindow("name"); 5 glutMouseFunc(&myMouse); 6 glutDisplayFunc(&display); 7 …… …… 8 }
- 對於縮放.
- 1 因為要涉及到之前顯示函式display()中的glLookAt()的改變,所以我們將其中的值設為全域性變數。
1 //全域性變數 2 static float place_z = 60.0f; 3 static float place_x = 0.0f; 4 //修改函式引數 5 void display(void) 6 { 7 …… …… 8 gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0); 9 glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋轉 10 …… …… 11 }
- 2 在之前的滑鼠的函式中加入對滾輪的控制語句
1 //全域性變數 2 #define GLUT_WHEEL_UP 3 3 #define GLUT_WHEEL_DOWN 4 4 //函式中 5 void myMouse(int button, int state, int x, int y) 6 { 7 …… …… 8 if (state == GLUT_UP && button == GLUT_WHEEL_UP) 9 { 10 glutReshapeFunc(NULL); 11 place_z -= 5.0; 12 display(); 13 } 14 if (state == GLUT_UP && button == GLUT_WHEEL_DOWN) 15 { 16 glutReshapeFunc(NULL); 17 place_z += 5.0; 18 display(); 19 } 20 }
- 這樣就ok啦。到這裡就完成了一開始提出四個目標以及一個reshape()函式。效果就如最開始的gif動畫一樣。
- 這裡還需要提到的一點是,動畫播放的速度在不同的cpu裡是不一樣的,如果太快或太慢可以通過myIdle函式的angle自增的大小來控制。
- 為了避免混亂,最後附上完整的原始碼。
1 #include <GL/glut.h> 2 #include <stdlib.h> 3 #include <stdio.h> 4 5 static const GLfloat vertex_list[][3] = { 6 - 15.0f, -20.0f, -10.0f, 7 40.0f, -20.0f, -10.0f, 8 40.0f, 20.0f, -10.0f, 9 -15.0f, 20.0f, -10.0f, 10 -15.0f, -20.0f, 10.0f, 11 40.0f, -20.0f, 10.0f, 12 -15.0f, 20.0f, 10.0f, 13 40.0f, 20.0f, 10.0f, 14 }; 15 GLuint texGround; 16 GLuint texWall; 17 18 #define BMP_Header_Length 54 19 static GLfloat angle = 0.0f; 20 static float place_z = 60.0f; 21 static float place_x = 0.0f; 22 #define GLUT_WHEEL_UP 3 23 #define GLUT_WHEEL_DOWN 4 24 25 // 函式power_of_two用於判斷一個整數是不是2的整數次冪 26 int power_of_two(int n) 27 { 28 if (n <= 0) 29 return 0; 30 return (n & (n - 1)) == 0; 31 } 32 33 /* 函式load_texture 34 * 讀取一個BMP檔案作為紋理 35 * 如果失敗,返回0,如果成功,返回紋理編號 36 */ 37 GLuint load_texture(const char* file_name) 38 { 39 GLint width, height, total_bytes; 40 GLubyte* pixels = 0; 41 GLuint last_texture_ID = 0, texture_ID = 0; 42 43 // 開啟檔案,如果失敗,返回 44 FILE* pFile; 45 errno_t err; 46 err = fopen_s(&pFile, file_name, "rb"); 47 if (!pFile) exit(0); 48 49 // 讀取檔案中圖象的寬度和高度 50 fseek(pFile, 0x0012, SEEK_SET); 51 fread(&width, sizeof(width), 1, pFile); 52 fread(&height, sizeof(height), 1, pFile); 53 fseek(pFile, BMP_Header_Length, SEEK_SET); 54 55 // 計算每行畫素所佔位元組數,並根據此資料計算總畫素位元組數 56 { 57 GLint line_bytes = width * 3; 58 while (line_bytes % 4 != 0) 59 ++line_bytes; 60 total_bytes = line_bytes * height; 61 } 62 63 // 根據總畫素位元組數分配記憶體 64 pixels = (GLubyte*)malloc(total_bytes); 65 if (pixels == 0) 66 { 67 fclose(pFile); 68 return 0; 69 } 70 71 // 讀取畫素資料 72 if (fread(pixels, total_bytes, 1, pFile) <= 0) 73 { 74 free(pixels); 75 fclose(pFile); 76 return 0; 77 } 78 79 // 對就舊版本的相容,如果圖象的寬度和高度不是的整數次方,則需要進行縮放 80 // 若影像寬高超過了OpenGL規定的最大值,也縮放 81 { 82 GLint max; 83 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); 84 if (!power_of_two(width) 85 || !power_of_two(height) 86 || width > max 87 || height > max) 88 { 89 const GLint new_width = 256; 90 const GLint new_height = 256; // 規定縮放後新的大小為邊長的正方形 91 GLint new_line_bytes, new_total_bytes; 92 GLubyte* new_pixels = 0; 93 94 // 計算每行需要的位元組數和總位元組數 95 new_line_bytes = new_width * 3; 96 while (new_line_bytes % 4 != 0) 97 ++new_line_bytes; 98 new_total_bytes = new_line_bytes * new_height; 99 100 // 分配記憶體 101 new_pixels = (GLubyte*)malloc(new_total_bytes); 102 if (new_pixels == 0) 103 { 104 free(pixels); 105 fclose(pFile); 106 return 0; 107 } 108 109 // 進行畫素縮放 110 gluScaleImage(GL_RGB, 111 width, height, GL_UNSIGNED_BYTE, pixels, 112 new_width, new_height, GL_UNSIGNED_BYTE, new_pixels); 113 114 // 釋放原來的畫素資料,把pixels指向新的畫素資料,並重新設定width和height 115 free(pixels); 116 pixels = new_pixels; 117 width = new_width; 118 height = new_height; 119 } 120 } 121 122 // 分配一個新的紋理編號 123 glGenTextures(1, &texture_ID); 124 if (texture_ID == 0) 125 { 126 free(pixels); 127 fclose(pFile); 128 return 0; 129 } 130 131 // 繫結新的紋理,載入紋理並設定紋理引數 132 // 在繫結前,先獲得原來繫結的紋理編號,以便在最後進行恢復 133 GLint lastTextureID = last_texture_ID; 134 glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID); 135 glBindTexture(GL_TEXTURE_2D, texture_ID); 136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 139 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 140 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); 141 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, 142 GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); 143 glBindTexture(GL_TEXTURE_2D, lastTextureID); //恢復之前的紋理繫結 144 free(pixels); 145 return texture_ID; 146 } 147 void display(void) 148 { 149 glEnable(GL_DEPTH_TEST); 150 glClearColor(0, 0, 0, 1); 151 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 152 153 glMatrixMode(GL_PROJECTION); 154 glLoadIdentity(); 155 gluPerspective(60.0, 1, 1.0, 100.0); 156 157 glMatrixMode(GL_MODELVIEW); 158 glLoadIdentity(); //載入單位矩陣 159 gluLookAt(place_x, 0.0, place_z, 0, 0, 0, 0.0, 1.0, 0); 160 glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋轉 161 162 glColor3f(1.0, 0, 0); 163 glutSolidSphere(6, 20, 20); 164 165 glColor3f(0.0, 0, 1.0); 166 glTranslatef(-20.0, 0, 0); 167 glutSolidSphere(3, 20, 20); 168 169 glColor3f(1.0, 1.0, 0); 170 glTranslatef(-6.0, 0, 0); 171 glutSolidSphere(1, 20, 20); 172 173 glEnable(GL_TEXTURE_2D); 174 texGround = load_texture("ground.bmp"); 175 texWall = load_texture("wall.bmp"); 176 177 //繪製底面 178 glBindTexture(GL_TEXTURE_2D, texGround); 179 glBegin(GL_QUADS); 180 glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[4]); 181 glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[5]); 182 glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[1]); 183 glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[0]); 184 glEnd(); 185 //繪製立面 186 glBindTexture(GL_TEXTURE_2D, texWall); 187 glBegin(GL_QUADS); 188 glTexCoord2f(0.0f, 0.0f); glVertex3fv(vertex_list[0]); 189 glTexCoord2f(1.0f, 0.0f); glVertex3fv(vertex_list[1]); 190 glTexCoord2f(1.0f, 1.0f); glVertex3fv(vertex_list[2]); 191 glTexCoord2f(0.0f, 1.0f); glVertex3fv(vertex_list[3]); 192 glEnd(); 193 glDisable(GL_TEXTURE_2D); 194 glutSwapBuffers(); 195 } 196 void myIdle(void) 197 { 198 angle += 1.8f; 199 if (angle >= 360.0f) 200 angle = 0.0f; 201 display(); 202 } 203 void myMouse(int button, int state, int x, int y) 204 { 205 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) 206 { 207 glutIdleFunc(&myIdle); 208 } 209 if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN) 210 { 211 glutIdleFunc(NULL); 212 } 213 if (state == GLUT_UP && button == GLUT_WHEEL_UP) 214 { 215 glutReshapeFunc(NULL); 216 place_z -= 5.0; 217 display(); 218 } 219 if (state == GLUT_UP && button == GLUT_WHEEL_DOWN) 220 { 221 glutReshapeFunc(NULL); 222 place_z += 5.0; 223 display(); 224 } 225 } 226 227 void reshape(int w, int h) 228 { 229 glViewport(0, 0, 500, 500); 230 glMatrixMode(GL_PROJECTION); 231 glLoadIdentity(); 232 gluPerspective(60.0, 1, 1, 100.0); 233 glMatrixMode(GL_MODELVIEW); 234 glLoadIdentity(); 235 gluLookAt(0, 0.0, 60.0, 0, 0, 0, 0.0, 1.0, 0); 236 } 237 int main(int argc, char** argv) 238 { 239 glutInit(&argc, argv); 240 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); 241 glutInitWindowSize(500, 500); 242 glutInitWindowPosition(100, 100); 243 glutCreateWindow("name"); 244 glutMouseFunc(&myMouse); 245 glutDisplayFunc(&display); 246 glutReshapeFunc(&reshape); 247 glutIdleFunc(&myIdle); 248 glutMainLoop(); 249 return 0; 250 }