您的位置:首页 > 运维架构

[OpenGL] 斯坦福兔子与显示列表

2016-04-21 19:32 363 查看


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个连续的显示列表,返回第一个显示列表。

glNewList(name,mode);
……
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: