[OpenGL] 斯坦福兔子与显示列表
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; }