- 實作任務目標:
- 使用紋理貼圖,增強可視效果
- 應用坐標變換,實作場景中不同物體重建
- 采用雙緩沖技術,實作場景實時繪制
- 具有一定的滑鼠、鍵盤互動功能
- 先放效果
滑鼠的互動功能有:右鍵暫停轉動,左鍵繼續轉動,滾輪向前放大,向后縮小
- 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軸垂直螢屏向外為正,符合右手定則,
- 2020/5/15 13:06:37 對于影像的各種變換做一個小的補充
- 視圖變換即設定/改變觀察點的位置,可以這么理解,相當于選擇一個位置和方向設定一臺照相機,針對glLookAt()函式而言,它一共有九個引數,3對坐標值,第一對三維坐標是觀察點(照相機)在世界坐標中的位置,第二對三維坐標是被觀察點(物體)的位置,從第一對坐標到第二對坐標的向量其實就指定了照相機的方向,比如說人站在臺階上,這是人在世界坐標的位置,然后人可以朝天空看,也可以朝地上看,可以朝北方看,也可以朝南方看,這個方向就是由兩對坐標所造成的向量來決定的,第三對坐標是人頭部的正向,可以指定人站著看,也可以倒立著看,類似于這樣,
- 模型變換則是改變物體本身的位置與方向,用到的函式有glTranslate,glRotate,glScale,這些都是對物體的坐標做變換的,相當于乘以一個變換矩陣,那么這里面有兩個需要注意的點,
- 1 變換的順序是逆向的,也就是說,如果我們寫的順序是先平移再旋轉,那么實際得到的結果應該是先旋轉再平移,所以我們可以用堆疊來實作,對于堆疊的概念不多作解釋了,如果不清楚可以去查資料結構,堆疊用到的函式是glPushMatrix()和glPopMatrix(),用法呢其實就是先宣告push,然后按照想要的順序寫好矩陣函式,最后Pop一下,就能得到想要的結果,
- 2 比如連續使用偏移函式,則第二個偏移的結果其實是在第一次作偏移的基礎上再做偏移的,那么如果我不想這樣算怎么辦呢?清空矩陣,用到的函式就是glLoadIdentity(),它的作用是把當前矩陣設定為單位矩陣,一般在開始做變換前都是需要呼叫一次這個函式的,
- 那么事實上,在OpenGL中,因為視圖變換和模型變換的效果是類似的,所以這兩個變換放在一個模式里面,在進行這兩種變換前,需要宣告glMatrixMode(GL_MODEVIEW),看到這個'Matrix'是不是很眼熟呢?沒錯,上面講堆疊函式的時候用到了,相信你們也會有個疑問,如果直接呼叫堆疊函式,這個堆疊是在哪里的呢?這個堆疊段不需要自己宣告了,但它其實是屬于這個模式的堆疊段,因為在接下來要將的投影變換的模式下也有自己的堆疊段,所以我也是從這個角度理解為什么要分為這兩個模式的原因,
- 投影變換事實上時指定了一個可視空間,相當于你在外部架好了照相機,但你仍然可以在照相機的鏡頭里設定要不要放大看到的景象,在教程【2】里有有關于這個的圖,所以首先我們要宣告模式glMatrixMode(GL_PROJECTION),并單位化矩陣glLoadIdentity(),
- 在這個模式里有透視投影和正投影(我們做3D一般用的都是透視投影),正投影的函式有glOrtho()和gluOrtho2D(),透視投影的函式有glFrustum()和gluPerspective(),我們最常用的當然就是gluPerspective()啦,
- gluPerspective()中的第一個引數是角度,它相當于人的眼皮要睜開多大,也就是2*仰角(仰角=俯角=1/2這個角度),第二個引數是比例,應該是跟顯示的螢屏的寬高比例有關(但是這點我不是很確定,只是暫時這么理解,如果有更好的解釋,歡迎在評論區提出),第三、四個引數則是表示截取的范圍,相當于兩堵墻,兩墻之間的東西能看,墻外的都忽略,這就是透視的意義,
- 那么最后就是視口變換,用到的函式是glViewport(),這個就不多做介紹了,
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 }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/5191.html
標籤:其他
上一篇:openCV從入門到放棄
