1. 程式人生 > >opengl簡單入門例項

opengl簡單入門例項

  • 實現任務目標:
    • 使用紋理貼圖,增強可視效果
    • 應用座標變換,實現場景中不同物體重建
    • 採用雙緩衝技術,實現場景實時繪製
    • 具有一定的滑鼠、鍵盤互動功能
  • 先放效果

滑鼠的互動功能有:右鍵暫停轉動,左鍵繼續轉動,滾輪向前放大,向後縮小

  • 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 }