计算机图形学 实验5 《显示列表》
一、实验目的
学习加快图形显示的显示列表技术。
二、实验内容
1、用显示列表输出文字;
2、用显示列表显示图形。
三、实验方法
OpenGL并没有直接提供显示文字的功能,并且,OpenGL也没有自带专门的字库。因此,要显示文字,就必须依赖操作系统所提供的功能。各种流行的图形操作系统,例如 Windows系统和 Linux系统,都提供了一些功能,以便能够在 OpenGL程序中方便的显示文字。
最常见的方法就是,我们给出一个字符,给出一个显示列表编号,然后操作系统由把绘制这个字符的 OpenGL命令装到指定的显示列表中。当需要绘制字符的时候,我们只需要调用这个显示列表即可。假如我们要显示的文字全部是 ASCII字符,则总共只有 0到 127这 128种可能,因此可以预先把所有的字符分别装到对应的显示列表中,然后在需要时调用这些显示列表即可。
四、实验步骤
1、准备好绘制文字/图形的显示列表,(如果输出文字)申请MAX_CHAR
个连续的显示列表编号,并把每个字符的绘制命令都装到对应的显示列表中;(如果输出图形)将绘制图形的各个函数装入显示列表;
2、绘制时执行显示列表,使用callList
函数。
五、实验结果与实验结论
实验输出图(部分是解释):
1、使用显示列表显示文字:
关键代码:
#define MAX_CHAR 128
void drawString(const char* str) {
static int isFirstCall = 1;
static GLuint lists;
if (isFirstCall) { // 如果是第一次调用,执行初始化
// 为每一个ASCII字符产生一个显示列表
isFirstCall = 0;
// 申请MAX_CHAR个连续的显示列表编号
lists = glGenLists(MAX_CHAR);
// 把每个字符的绘制命令都装到对应的显示列表中
wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);
}
// 调用每个字符对应的显示列表,绘制每个字符
for (; *str != '\0'; ++str)
glCallList(lists + *str);
}
void selectFont(int size, int charset, const char* face) {
HFONT hFont = CreateFontA(size, 0, 0, 0, FW_MEDIUM, 0, 0, 0,
charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, face);
HFONT hOldFont = (HFONT)SelectObject(wglGetCurrentDC(), hFont);
DeleteObject(hOldFont);
}
void display(void) {
selectFont(48, ANSI_CHARSET, "Comic Sans MS");
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0f, 1.0f, 1.0f);
glRasterPos2f(0.0f, 0.0f);
drawString("Hello FALL GUYS!");
glutSwapBuffers();
}
void init(void)
{
glClearColor(0.0, 0.0, 0.0, 0.0); /* select clearing olor */
// glMatrixMode(GL_PROJECTION); /* initialize viewing values */
// glLoadIdentity();
// glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); //注意该视景体的范围和几何中心
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(500, 500); //改为glutInitWindowSize (250, 250); 可以看出这个变换仅仅是物体按比例大小的变换
glutInitWindowPosition(100, 100);
glutCreateWindow("First");
init();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
2、使用显示列表显示图形
关键源码:
GLuint listName;
static void init(void)
{
listName = glGenLists(1);
glNewList(listName, GL_COMPILE);
glColor3f(1.0, 0.0, 0.0); /* current color red */
glBegin(GL_TRIANGLES);
glVertex2f(0.0, 0.0);
glVertex2f(1.0, 0.0);
glVertex2f(0.0, 1.0);
glEnd();
glTranslatef(1.5, 0.0, 0.0); /* move position */
glEndList();
glShadeModel(GL_FLAT);
}
static void drawLine(void)
{
glBegin(GL_LINES);
glVertex2f(0.0, 0.5);
glVertex2f(15.0, 0.5);
glEnd();
}
void display(void)
{
if (1) {
GLuint i;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 1.0, 0.0); /* current color green */
for (i = 0; i < 10; i++) /* draw 10 triangles */
glCallList(listName);
drawLine(); /* is this line green? NO! */
/* where is the line drawn? */
glFlush();
}
}
void reshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
gluOrtho2D(0.0, 2.0, -0.5 * (GLfloat)h / (GLfloat)w,
1.5 * (GLfloat)h / (GLfloat)w);
else
gluOrtho2D(0.0, 2.0 * (GLfloat)w / (GLfloat)h, -0.5, 1.5);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void keyboard(unsigned char key, int x, int y)
{
switch (key) {
case 27:
exit(0);
break;
}
}
/* Main Loop
* Open window with initial window size, title bar,
* RGBA display mode, and handle input events.
*/
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(650, 50);
glutCreateWindow(argv[0]);
init();
glutReshapeFunc(reshape);
glutDisplayFunc(display);
glutKeyboardFunc(keyboard);
glutMainLoop();
return 0;
}
六、实验小结
本次实验主要是了解了显示列表的装载、callList
函数的使用等。使用显示列表一般有四个步骤:分配一个未使用的显示列表编号,把OpenGL函数装入显示列表来创建显示列表,调用显示列表,销毁显示列表。显示列表一旦产生就一直存在(除非调用glDeleteLists
函数进行销毁),所以我们只需要在第一次调用的时候初始化,以后就可以很方便的调用这些显示列表来绘制字符了。Windows系统中,可以使用wglUseFontBitmaps
函数来批量的产生显示字符用的显示列表。
在图形图像中显示列表表示一组存储在一起的OpenGL函数,可以创建后再多次调用,以提高绘图效率,节省计算机的处理资源。调用一个显示列表时,它所存储的函数就会按照顺序执行。使用显示列表,可以一次定义几何图形(或状态更改),并在以后多次执行它们。
显示列表的局限性在于是存储OpenGL函数调用的一种高速的缓存,而非动态的数据仓库,因此显示列表不能够进行动态的修改。例如,创建物体的变形动画或者删除某些定点的图元时,都需要销毁和重新建立相应的显示列表,反而因此降低了动态模型绘制的性能。并且由于调用显示列表时程序本身也有一些开销,尤其是当一个显示列表太小会导致系统开销超过列表的优越性,所以并不是只要调用显示列表就能优化程序性能。