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

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;
}
时间: 2024-08-07 10:15:35

[OpenGL] 斯坦福兔子与显示列表的相关文章

OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别

OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间使用.OpenGL3.0已经废弃此方法.每个glVertex与GPU进行一次通信,十分低效. glBegin(GL_TRIANGLES); glVertex(0, 0); glVertex(1, 1); glVertex(2, 2); glEnd(); 2.显示列表(glCallList) 每个gl

[OpenGL] 兔子与显示列表

1.调整桌子的大小.         在OpenGL绘制长方体,可以通过函数: glutSolidCube(Size)          绘制得到的是一个正方体,再利用缩放矩阵使其变成长方体,使得桌子的大小刚好可以放下16只兔子. 2.兔子的增多减少 使用一个全局变量rabbitNum来记录兔子的数量. 在键盘回调函数中,在按下I,K后令rabbitNum增加或减少,并维护兔子的数量在1~16,等于16或1不再进行相应操作. 绘制兔子时,通过循环控制,每画完一只兔子,平移一段距离,画到第4i+1

OpenGL显示列表

OpenGL显示列表(Display List)是由一组预先存储起来的留待以后调用的OpenGL函数语句组成的,当调用这张显示列表时就依次执行表中所列出的函数语句.前面内容所举出的例子都是瞬时给出函数命令,则OpenGL瞬时执行相应的命令,这种绘图方式叫做立即或瞬时方式(immediate mode).本章将详细地讲述显示列表的基本概论.创建.执行.管理以及多级显示列表的应用等内容. 16.1.显示列表概论     16.1.1 显示列表的优势 OpenGL显示列表的设计能优化程序运行性能,尤其

显示列表

显示列表一组已经存储(编译)的OpenGL命令,以供后续执行.一旦显示列表创建出来,所有顶点与像素数据都被赋值且拷贝到服务器段的显示列表内存.这是一次性过程.在显示列表配备(编译)好后,你可以重复使用它,而不需要每帧都重新赋值与重新反复传递数据.显示列表是绘制静态数据的最快方式之一,这是因为顶点数据与OpenGL命令被存储在显示列表中并且最小化客户端到服务器段的数据传输.也就是说,它降低CPU处理实际数据传输的周期. 显示列表的另一个重要性能是:由于显示列表是服务端数据,它可以在多个客户端共享.

NeHe OpenGL教程 第十二课:显示列表

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第十二课:显示列表 显示列表: 想知道如何加速你的OpenGL程序么?这一课将告诉你如何使用OpenGL的显示列表,它通过预编译OpenGL命令来加速你的程序,并可以为你省去很多重复的代码. 这次我将教你如何使用显示列表,显示列表将

第12课 OpenGL 显示列表

显示列表: 想知道如何加速你的OpenGL程序么?这一课将告诉你如何使用OpenGL的显示列表,它通过预编译OpenGL命令来加速你的程序,并可以为你省去很多重复的代码. 这次我将教你如何使用显示列表,显示列表将加快程序的速度,而且可以减少代码的长度. 当你在制作游戏里的小行星场景时,每一层上至少需要两个行星,你可以用OpenGL中的多边形来构造每一个行星.聪明点的做法是做一个循环,每个循环画出行星的一个面,最终你用几十条语句画出了一个行星.每次把行星画到屏幕上都是很困难的.当你面临更复杂的物体

计算机图形学(二)输出图元_18_显示列表_1_创建和命名OpenGL显示表

OpenGL显示列表 把对象描述成一个命名的语句序列(或任何其他的命令集)并存储起来既方便又高效.在OpenGL中使用称为显示表(display list)的结构可以做到这一点.一旦建立了显示表,就可以用不同的显示操作来多次引用该表.在网格中,描述图形的显示表存放在服务器中,以避免每次显示场景时都要传送表中的命令.我们可以为以后的执行来建立并存储显示表,或指定表中的命令立即执行.显示表对层次式建模特别有用,因为一个复杂的对象可以用一组简单的对象来描述. 创建和命名OpenGL显示表 使用glNe

尝试使用Osg编译上下文实现多线程编译显示列表--总结

在realize()前打开预编译选项指令: osg::DisplaySettings::instance()->setCompileContextsHint(true);    mpr_osgviewer->realize(); 显示如下信息: 此时虽然trait::shareContext变量有了值,但是实际上其共享图形上下文句柄hglrc是没有创建成功的. 我试图自己创建opengl图形共享上下文hglrc以实现多线程预编译显示列表,避免帧冲击,可惜无法成功. osg::GraphicsC

字符图元 &amp; 显示列表

[字符图元] 1.typeface(字样),即设计风格,如Courier等. 2.font(字体),如10磅Courier斜体. 3.monspace即为等宽字体,proportional为非等宽字体. 4.字体有2种存储格式:位图字体(bitmap font),也要光栅字体(raster font):轮廓字体(outline font)或笔划字体(stroke font). 5.GLUT位图字体由OpenGL的glBitmap函数来绘制,而轮廓字体由折线边界(GL_LINE_STRIP)生成.