1.調整桌子的大小。
在OpenGL繪制長方體,能夠通過函數:
glutSolidCube(Size)繪制得到的是一個正方體,再利用縮放矩陣使其變成長方體。使得桌子的大小剛好能夠放下16僅僅兔子。
2.兔子的增多降低
使用一個全局變量rabbitNum來記錄兔子的數量。
在鍵盤回調函數中,在按下I,K后令rabbitNum添加或降低,並維護兔子的數量在1~16,等於16或1不再進行對應操作。
繪制兔子時。通過循環控制,每畫完一僅僅兔子,平移一段距離,畫到第4i+1僅僅兔子時。平移到下一行。
在普通繪制模式下,直接調用繪制桌子、兔子函數。
在顯示列表繪制模式下。調用事先准備好的桌子顯示列表和兔子顯示列表來繪制。
3.FPS
fps的含義是每秒傳輸幀數,它的大小反映了繪制的流暢性。在這里我們須要計算普通以及顯示列表模式下fps大小的差別。
計算fps。我們能夠調用該函數:
glutGet( GLUT_ELAPSED_TIME );該函數返回兩次調用glutGet(GLUT_ELAPSED_TIME)的時間間隔,單位為毫秒。
通過得到兩次調用的間隔時間,我們能夠計算繪制圖像的刷新幀率。
我們用frame變量存儲幀數,兩次間隔調用時間差大於1000ms時我們更新fps的值。依照定義fps = 幀數/時間。
調用例如以下函數:
glutBitmapCharacter( GLUT_BITMAP_HELVETICA_18 , *c);能夠把字符變成位圖顯示在窗體中。
4.顯示列表
通過調用:
glGenLists(int num) 我們得到了num個連續的顯示列表,返回第一個顯示列表。
……
glEndList();
在這兩者之間,寫要增加顯示列表的詳細語句。
類似於數組。下一個顯示列表僅僅需移動偏移值1就能夠了得到名稱了。
glCallList(name);
調用顯示列表。
實驗數據記錄和處理
通過實驗,我們發如今使用了顯示列表的情況下,每秒刷新的幀率更高。它是一種快速的緩存,當我們須要重復地繪制同一物體時,能夠將其存入顯示列表,並在繪制時調用顯示列表,這樣就避免了由於重復調用而導致的重復計算。
缺點是不能動態繪制,也就是說我們后來在外面改動了一些變量的值,是不會影響到顯示列表的。詳細到此次實驗來說,我們不能把繪制多次兔子的循環放入顯示列表,由於循環的次數會隨着兔子次數的變化而變化,而一旦載入入了顯示列表,這樣的動態是無法體現出來的,顯示列表僅僅會存儲最初始增加時的狀態。所以我們僅僅能把繪制兔子的函數放入顯示列表,而在普通的函數中進行循環控制。
// glutEx1.cpp : 定義控制台應用程序的入口點。 // //注意FPS函數的應用 #include <stdlib.h> #include "glut.h" #include <stdio.h> #include <string.h> #include "stanford_bunny.h" float eye[] = {0, 4, 6}; //眼睛位置 float center[] = {0, 0, 0}; //視點位置 float fDistance = 0.2f; //距離因子 float fRotate = 0; //旋轉因子 bool bAnim = false; //是否旋轉 bool bDrawList = false; //是否使用顯示列表 GLint tableList=0; //桌子列表 GLint rabbitList = 0; //兔子列表 int rabbitNum = 1; //兔子數量 //繪制桌子 void DrawTable() { glPushMatrix(); glTranslatef(0, 3.5, 0); glScalef(5, 1, 4); glPushMatrix(); glScalef(1.3, 0.4, 1.3); glutSolidCube(1.0); glPopMatrix(); glPopMatrix(); glPushMatrix(); glTranslatef(2.0, 0.5, 1.5); glScalef(0.6, 5, 0.6); glutSolidCube(1.0); glPopMatrix(); glPushMatrix(); glTranslatef(-2.0, 0.5, 1.5); glScalef(0.6, 5, 0.6); glutSolidCube(1.0); glPopMatrix(); glPushMatrix(); glTranslatef(2.0, 0.5, -1.5); glScalef(0.6, 5, 0.6); glutSolidCube(1.0); glPopMatrix(); glPushMatrix(); glTranslatef(-2.0, 0.5, -1.5); glScalef(0.6, 5, 0.6); glutSolidCube(1.0); glPopMatrix(); } GLint GenTableList() { GLint lid=glGenLists(2); //生成兩個空的顯示列表 glNewList(lid, GL_COMPILE); // 用於創建和替換一個顯示列表函數原型 // 指定顯示列表的名稱,編譯模式:僅僅編譯 //第一個顯示列表:畫桌子 DrawTable(); glEndList(); //第二個顯示列表:畫兔子 glNewList(lid + 1, GL_COMPILE); DrawBunny(); glEndList(); return lid; //返回顯示列表編號 } void Draw_Table_List() { glPushMatrix(); glTranslatef(2.2, 4.5, 1.5); //平移與縮放 glScalef(2, 2, 2); for (int i = 1; i <= rabbitNum; i++) { glCallList(rabbitList); //調用顯示列表畫兔子 if (i == 4 || i == 8 || i == 12) { glTranslatef(2.1f, 0, -0.5f); //兔子換行 } else glTranslatef(-0.7f, 0.0f, 0.0f); //兔子平移 } glPopMatrix(); glCallList(tableList); //調用顯示列表畫桌子 } void DrawScene() { glPushMatrix(); glTranslatef(2.2, 4.5, 1.5); glScalef(2, 2, 2); for (int i = 1; i <= rabbitNum; i++) { DrawBunny(); //直接繪制兔子 if (i==4||i==8||i==12) { glTranslatef(2.1f, 0, -0.5f); //兔子換行 } else glTranslatef(-0.7f, 0.0f, 0.0f); //兔子平移 } glPopMatrix(); DrawTable(); //直接繪制桌子 } void reshape(int width, int height) { if (height == 0) { height = 1; //高度為0時,讓高度為1 } glViewport(0, 0, width, height); //設置視窗大小 glMatrixMode(GL_PROJECTION); //設置矩陣模式為投影 glLoadIdentity(); //初始化矩陣為單位矩陣 float whRatio = (GLfloat)width / (GLfloat)height; gluPerspective(45, whRatio, 1, 1000); //設置投影方位 glMatrixMode(GL_MODELVIEW); //設置矩陣模式為模型 } void idle() { glutPostRedisplay(); //調用當前繪制函數 } void key(unsigned char k, int x, int y) { switch(k) { case 27: case 'q': {exit(0); break; } case 'a': //物體左移 { eye[0] += fDistance; center[0] += fDistance; break; } case 'd': //物體右移 { eye[0] -= fDistance; center[0] -= fDistance; break; } case 'w': //物體上移 { eye[1] -= fDistance; center[1] -= fDistance; break; } case 's': //物體下移 { eye[1] += fDistance; center[1] += fDistance; break; } case 'z': //靠近 { eye[2] *= 0.95; break; } case 'c': //遠離 { eye[2] *= 1.05; break; } case 'l': // 切換顯示列表和非顯示列表繪制方式 { bDrawList = !bDrawList; break; } case ' ': //旋轉 { bAnim = !bAnim; break; } case 'i': //兔子增多 { if(rabbitNum<=15)rabbitNum++; break; } case 'k': //兔子降低 { if (rabbitNum>=2)rabbitNum--; break; } default: break; } } void getFPS() { static int frame = 0, time, timebase = 0; static char buffer[256]; //字符串緩沖區 char mode[64]; //模式 if (bDrawList) //是否用顯示列表繪制 strcpy(mode, "display list"); else strcpy(mode, "naive"); frame++; time=glutGet(GLUT_ELAPSED_TIME); //返回兩次調用glutGet(GLUT_ELAPSED_TIME)的時間間隔,單位為毫秒 if (time - timebase > 1000) { //時間間隔差大於1000ms時 sprintf(buffer,"FPS:%4.2f %s", frame*1000.0/(time-timebase), mode); //寫入buffer中 timebase = time; //上一次的時間間隔 frame = 0; } char *c; glDisable(GL_DEPTH_TEST); // 禁止深度測試 glMatrixMode(GL_PROJECTION); // 選擇投影矩陣 glPushMatrix(); // 保存原矩陣 glLoadIdentity(); // 裝入單位矩陣 glOrtho(0,480,0,480,-1,1); // 位置正投影 glMatrixMode(GL_MODELVIEW); // 選擇Modelview矩陣 glPushMatrix(); // 保存原矩陣 glLoadIdentity(); // 裝入單位矩陣 glRasterPos2f(10,10); for (c=buffer; *c != '\0'; c++) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, *c); //繪制字符 } glMatrixMode(GL_PROJECTION); // 選擇投影矩陣 glPopMatrix(); // 重置為原保存矩陣 glMatrixMode(GL_MODELVIEW); // 選擇Modelview矩陣 glPopMatrix(); // 重置為原保存矩陣 glEnable(GL_DEPTH_TEST); // 開啟深度測試 } /*畫圖函數*/ void redraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除顏色和深度緩存 glClearColor(0, 0.5, 0, 1); //設置清除顏色 glLoadIdentity(); //初始化矩陣為單位矩陣 gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0); // 場景(0,0,0)的視點中心 (0, 5, 50)。Y軸向上 // 視點位置。望向位置,頭頂方向 glEnable(GL_DEPTH_TEST); //開啟深度測試 glEnable(GL_LIGHTING); //開啟光照 GLfloat gray[] = { 0.4, 0.4, 0.4, 1.0 }; //設置灰色 GLfloat light_pos[] = {10, 10, 10, 1}; //設置光源位置 glLightModelfv(GL_LIGHT_MODEL_AMBIENT,gray); //指定整個場景的環境光強度 glLightfv(GL_LIGHT0, GL_POSITION, light_pos); //設置第0號光源的光照位置 glLightfv(GL_LIGHT0, GL_AMBIENT, gray); //設置第0號光源多次反射后的光照顏色(環境光顏色) glEnable(GL_LIGHT0); //開啟第0號光源 if (bAnim) fRotate += 0.5f; //改變旋轉因子 glRotatef(fRotate, 0, 1.0f, 0); //繞y軸旋轉 glScalef(0.4, 0.4, 0.4); //縮放 if(!bDrawList) DrawScene(); // 普通繪制 else Draw_Table_List(); // 顯示列表繪制 getFPS(); //得到每秒傳輸幀數(Frames Per Second) glutSwapBuffers(); //交換緩沖區 } int main (int argc, char *argv[]) { glutInit(&argc, argv);//初始化glut glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); //初始化顯示模式:RGB顏色模型,雙緩沖,深度測試 glutInitWindowSize(480,480);//設置窗體大小 int windowHandle = glutCreateWindow("Exercise 4"); //取得新建窗體的句柄 glutDisplayFunc(redraw);//注冊顯示函數 glutReshapeFunc(reshape); //注冊重繪函數 glutKeyboardFunc(key); //注冊鍵盤回調事件 glutIdleFunc(idle); //注冊空暇回調事件 tableList = GenTableList(); //初始化顯示列表 rabbitList = tableList + 1; glutMainLoop(); //開啟時間循環 return 0; }