显示列表

目的: 1. 如何利用显示列表表示直接模式下的函数来组织数据和提高性能。

             2. 通过理解使用以及何时使用显示列表来最大限度的优化性能。

 

显示列表是存储起来用于稍后执行的一组OPENGL 命令。激活一个显示列表后,就按照显示列表中预先排好的次序执行其存储的命令。

大部分的opengl命令即可以存储在显示列表中,也可以在直接模式中直接发送,直接发送的命令将被立即执行。

 

在程序中可以自由的混合使用显示列表和直接模式。

那么我们为什么要用显示列表呢,肯定有好处的吧? 那是,现在就来告诉你哦: 假如你在程序中要频繁的绘制一个几何物体,那么可以把绘制该几何物体的一系列命令放在

显示列表中,因为一些图形硬件也许会在专用的内存中存储显示列表,或者以一种优化的形式存储数据。

我们来看一个例子呀:

#include <cmath>
#include <gl\glut.h>
#include <Windows.h>

GLuint  theTorus;

static void torus(int numc, int numt)
{
    double TwoPI = 2 * 3.1415926;

    for (int i = 0; i < numc; ++i)
    {
        glBegin(GL_QUAD_STRIP);
        for (int j = 0; j <= numt; ++j)
        {
            for (int k = 1; k >= 0; --k)
            {
                double s = (i + k) % numc + 0.5;
                double t = j%numt;

                double x = (1 + 0.1*cos(s*TwoPI / numc))*cos(t*TwoPI / numt);
                double y = (1 + 0.1*cos(s*TwoPI / numc))*sin(t*TwoPI / numt);
                double z = 0.1 + sin(s*TwoPI / numc);
                
                glVertex3f(x, y, z);
            }
        }
        glEnd();
    }

}

static void init()
{
    theTorus = glGenLists(1);
    glNewList(theTorus, GL_COMPILE);
    torus(8, 25);
    glEndList();

    glShadeModel(GL_FLAT);
    glClearColor(0, 0, 0, 0);

}

void display()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, .0, .0);
    glCallList(theTorus);
    glFlush();
}

void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(30, width / height, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, 0,  // eyes
              0, 0, 0,  // center
              0, 1, 0); // up
}

void keyboard(unsigned char key, int x, int y)
{
    switch (key)
    {
    case 'x':
    case 'X':
        glRotatef(30, 1.0, 0.0, 0);
        glutPostRedisplay();
        break;
    case 'y':
    case 'Y':
        glRotatef(30, 0.0, 1.0, 0);
        glutPostRedisplay();
        break;
    case 'i':
    case 'I':
        glLoadIdentity();
        gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0);
        glutPostRedisplay();
        break;
    case 27:
        exit(0);
        break;
    }
}

int main(int argc, char* argv[])
{
    glutInitWindowSize(500, 500);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutCreateWindow("CallList");
    init();
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
    glutDisplayFunc(display);
    glutMainLoop();
	return 0;
}

上面的由glNewList 和 glEndList() 函数来定义一个显示列表。 而glNewList 函数的变量是ListName 是一个整数索引,是由 glGenList函数生成,表示该显示列表独一无二。

  显示列表的设计准则:

为了优化性能, OPENGL 的显示列表是对命令的缓存而不是动态数据库。

显示列表不会比直接执行其中的命令更慢, 当然显示列表会引进一些额外的开销, 如果一个列表不大,其开销可能超过其带来的优越性。

以下列出优化的可能操作:

1. 矩阵操作, 大部分的矩阵操作要求opengl进行求逆运算。

2. 光栅位图和图像。 所指定的光栅数据格式不可能像硬件那样理想。当编译显示列表时, Opengl可以将数据转变成硬件期望表示法。

3. 光照、材质属性以及光照模型。当在福州的光照条件下绘制场景时, 可能需要改变场景中的物体的材质。材质的设置涉及大量的计算,所以速度很慢。

4.纹理。在OPENGL 1.0版本中,显示列表是管理纹理的主要方法,然而在 1.1以上版本中, 那么应该在纹理对象中存储纹理。

5. 多边形点画模式。

显示列表的缺点:是显示列表内容的不变性。为了优化性能,opengl的显示列表的内容是不可修改的,而且内容也是不可读的。

上面的代码中,我们看到,命名和创建一个显示列表:

因为每一个显示列表都由一个任意的整数来索引。所以我们要保证索引的唯一性, 可以使用GLuint glGenLists (GLsizei range);该函数分配range个相邻的、未曾分配过的显示列表索引, 返回的整数是一个索引值, 如 GLuint Index = glGenLists (3);则Index , Index+1, Index+2都可以用来创建一个新的列表。 如果使用一个已用的显示列表索引, 将自动删除已存在的显示列表。

glNewList(Index, GL_COMPILE); 或者glNewList(Index+1, GL_COMPILE); .。。。。

然后以glEndList()结束。

void glCallList (GLuint list); 来调用显示列表。

可以使用glIsList(Index)函数来判定是否正在被应用。

使用void glDeleteLists (GLuint list, GLsizei range); 删除显示列表,并释放被占用的索引。 如: glDeleteLists(Index, 3);

可以在显示列表 glNewList 和 glEndList 之间多次调用其他索引的glCallList函数。

 

执行多显示列表:

来看一下例子:

namespace
{
    const int PT = 1;
    const int STROKE = 2;
    const int END = 3;
}

typedef struct charpoint
{
    GLfloat x, y;
    int type;
}CP;

CP Adata[] = {
    { 0, 0, PT }, { 0, 9, PT }, {1, 10, PT}, 
    { 4, 10, PT }, { 5, 9, PT }, {5, 0, STROKE},
    { 0, 5, PT }, {5, 5, END}
};

CP Edata[] = {
    { 5, 0, PT }, { 0, 0, PT }, {0, 10, PT},
    { 5, 10, STROKE }, { 0, 5, PT }, {4, 5, END}
};

CP Pdata[] = {
    { 0, 0, PT }, { 0, 10, PT }, {4, 10, PT}, 
    { 5, 9, PT }, { 5, 6, PT }, { 4, 5, PT }, {0, 5, END}
};

CP Rdata[] = {
    { 0, 1, PT }, {0, 10, PT }, {4, 10, PT},
    { 5, 9, PT }, { 5, 6, PT }, { 4, 5, PT },
    { 0, 5, STROKE }, { 3, 5, PT }, { 5, 0, END }
};

static void drawLetter(CP *l)
{
    glBegin(GL_LINE_STRIP);
    while (l)
    {
        switch (l->type)
        {
        case PT:
            glVertex2fv(&l->x);
            break;
        case STROKE:
            glVertex2fv(&l->x);
            glEnd();
            glBegin(GL_LINE_STRIP);
            break;
        case END:
            glVertex2fv(&l->x);
            glEnd();
            glTranslatef(8.0, 0, 0);
            return;
        }
        ++l;
    }
}

static void init()
{
    GLuint base;
    glShadeModel(GL_FLAT);

    base = glGenLists(128);
    glListBase(base);
    
    glNewList(base + 'A', GL_COMPILE); drawLetter(Adata);
    glEndList();

    glNewList(base + 'E', GL_COMPILE); drawLetter(Edata);
    glEndList();

    glNewList(base + 'P', GL_COMPILE); drawLetter(Pdata);
    glEndList();

    glNewList(base + 'R', GL_COMPILE); drawLetter(Rdata);
    glEndList();

    glNewList(base + ' ', GL_COMPILE); glTranslatef(8, 0, 0); glEndList();
}

static void printStrokedString(const std::string& str)
{
    GLsizei length = str.length();
    glCallLists(length, GL_BYTE, (GLbyte*)(str.data()));
}

我们可以看到调用glCallLists 第三个参数是直接把每个字符值当作索引, 但是我们看到的索引是base+ 字符值, 所以调用了函数

glListBase, 然后指定了他的索引初始值。

void glCallLists (GLsizei n, GLenum type, const GLvoid *lists);

n-------执行n个显示列表

type------指明参数lists中数值的数据类型

lists------要读取的索引值存放的数组指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值