关闭

VBO的使用2

标签: bufferlistkeyboardobject测试扩展
705人阅读 评论(0) 收藏 举报
分类:

OpenGL VBO 说明

自: http://hi.baidu.com/luckwxx623/blog/item/34e36445b8fad92ecffca3e1.html

 
顶点缓冲和索引缓冲是OpenGL 1.5版本所提供的功能,因此首先检查OpenGL版本是否达到1.5。因为Windows仅直接支持OpenGL 1.1的函数,更高版本的函数应该使用wglGetProcAddress函数来获得这些函数的指针,然后利用函数指针进行间接调用。而且这些函数所需要使用的常量在一个叫做glext.h的头文件中定义,可在google上搜索下载该文件的最新版本。

Vertex Buffer Object(顶点缓冲对象)所涉及的函数:

glGenBuffers:分配缓冲对象编号

glBindBuffer:绑定缓冲。可以绑定顶点缓冲和索引缓冲。

glBufferData,glBufferSubData:修改缓冲中的全部数据或部分数据。第一次修改时应该使用glBufferData,以后可以选择使用glBufferData或glBufferSubData。

glMapBuffer,glUnmapBuffer:glMapBuffer锁定缓冲数据并把缓冲数据映射到内存,然后可以用一个指针进行灵活的访问和修改,修改完成后利用glUnmapBuffer确认修改并解除锁定。


顶点缓冲/索引缓冲使用示例。

注意:该程序使用C语言编写(不是C++)。使用了两个工具包,GLUT和GLEE。其中:GLUT的安装方法在本课程的第一课里面有描述。GLEE实际上就是两个文件glee.h和glee.c,从网上下载这两个文件的最新版本并放到工程中,和下面的代码一起编译。


代码过长,分开发送。

#define WindowWidth   512

#define WindowHeight 512

#define WindowTitle  "OpenGL — Vertex Buffer Objects 测试"


#include "GLee.h"

#include <GL/glut.h>

#include <stdio.h>


static GLfloat gf_RotateAngle = 0.0f;

static int      gi_Rotating = 1;

void display(void)
{
#define length_half (1.0f)

     // 混合数组,用六个值表示一个顶点(前三项为颜色,后三项为顶点坐标),共8个顶点

    static GLfloat vertex_list[6*8] =
     {
        0.0f, 0.0f, 0.0f, -length_half, -length_half, -length_half,

         0.0f, 0.0f, 1.0f, -length_half, -length_half,   length_half,

         0.0f, 1.0f, 0.0f, -length_half,   length_half, -length_half,

         0.0f, 1.0f, 1.0f, -length_half,   length_half,   length_half,

         1.0f, 0.0f, 0.0f,   length_half, -length_half, -length_half,

         1.0f, 0.0f, 1.0f,   length_half, -length_half,   length_half,

         1.0f, 1.0f, 0.0f,   length_half,   length_half, -length_half,

         1.0f, 1.0f, 1.0f,   length_half,   length_half,   length_half

     };
     // 索引数组,每四个顶点表示一个平面,共六个平面

    static GLuint index_list[4*6] =
     {
         0, 1, 3, 2,
         4, 5, 7, 6,
         0, 2, 6, 4,
         1, 3, 7, 5,
         0, 1, 5, 4,
         2, 3, 7, 6
     };
     // 标记本函数是否为第一次调用,如果是,可进行初始化
    static int isFirstCall = 1;
#undef length_half
// 清除屏幕
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     glEnable(GL_DEPTH_TEST);

     // 设置视角
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     gluPerspective(60, 1, 1, 10);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     gluLookAt(3, 3, 3, 0, 0, 0, 0, 1, 0);

    // 旋转
     glRotatef(gf_RotateAngle, 0.0f, 1.0f, 1.0f);
     // 初始化GLEE,GLEE会自动读取动态连接库中的1.1以上版本OpenGL函数(如果有的话)
     GLeeInit();
     // 根据OpenGL所支持VBO的情况,有三种方式执行渲染
    if( _GLEE_VERSION_1_5 ) // 支持OpenGL 1.5,使用标准的VBO函数
     {
        if( isFirstCall )
         {
             GLuint iVertexBuffer = 0;
             GLuint iIndexBuffer   = 0;

             // 设置顶点缓冲
             glGenBuffers(1, &iVertexBuffer);
             glBindBuffer(GL_ARRAY_BUFFER, iVertexBuffer);
             glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_list),
                 vertex_list, GL_STATIC_DRAW);

             // 设置索引缓冲
             glGenBuffers(1, &iIndexBuffer);
             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iIndexBuffer);
             glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_list),
                 index_list, GL_STATIC_DRAW);


             // 其它设置与顶点数组的设置方式类似,只有在设置指针时不同
             // 不要直接使用指针,而使用偏移地址
             // vertex_list中的数据已经被保存到iVertexBuffer所对应的缓冲对象中,因此使用零作为偏移地址
             // 如果缓冲对象中有很多杂乱的数据,则通过指定不同的偏移地址即可选择不同数据,不需要重新绑定
             glEnableClientState(GL_VERTEX_ARRAY);
             glEnableClientState(GL_COLOR_ARRAY);
             glInterleavedArrays(GL_C3F_V3F, 0, 0);
         }
         // 不要直接使用指针,而使用偏移地址
         glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, 0);
     }

    else if( _GLEE_ARB_vertex_buffer_object ) // 不支持OpenGL 1.5,但以ARB扩展的形式支持VBO
     {
         // 与标准形式的VBO几乎相同
         // 只是函数名有无ARB后缀,常量名有无_ARB后缀这一点区别

        if( isFirstCall )
         {
             GLuint iVertexBuffer = 0;
             GLuint iIndexBuffer   = 0;
             glGenBuffersARB(1, &iVertexBuffer);
             glBindBufferARB(GL_ARRAY_BUFFER_ARB, iVertexBuffer);
             glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertex_list),
                 vertex_list, GL_STATIC_DRAW_ARB);

             glGenBuffersARB(1, &iIndexBuffer);
             glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, iIndexBuffer);
             glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(index_list),
                 index_list, GL_STATIC_DRAW_ARB);

             glEnableClientState(GL_VERTEX_ARRAY);
             glEnableClientState(GL_COLOR_ARRAY);
             glInterleavedArrays(GL_C3F_V3F, 0, 0);
         }
         glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, 0);
     }
    else // 不支持VBO,使用Vertex Array代替
     {
        if( isFirstCall )
         {
            printf("your system does not support the VBO.\n");
             glEnableClientState(GL_VERTEX_ARRAY);
             glEnableClientState(GL_COLOR_ARRAY);
             glInterleavedArrays(GL_C3F_V3F, 0, vertex_list);
         }
         glDrawElements(GL_QUADS, 24, GL_UNSIGNED_INT, index_list);
     }
     // 交换缓冲
     glutSwapBuffers();
    isFirstCall = 0;
}
void idle(void)
{
    if( gi_Rotating )
     {
         gf_RotateAngle += 0.1f;
        if( gf_RotateAngle >= 360.0f )
             gf_RotateAngle = 0.0f;
     }
     display();
}
void keyboard(unsigned char c, int x, int y)
{
     gi_Rotating = !gi_Rotating;
}
int main(int argc, char* argv[])
{
     // GLUT初始化
     glutInit(&argc, argv);
     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
     glutInitWindowPosition(100, 100);
     glutInitWindowSize(WindowWidth, WindowHeight);
     glutCreateWindow(WindowTitle);
     glutDisplayFunc(&display);
     glutIdleFunc(&idle);
     glutKeyboardFunc(&keyboard);  
     glutMainLoop();
    return 0;
}



顶点缓冲对象(Vertex Buffer Object, VBO)的使用步骤如下:

1、用glGenBuffers分配缓冲对象编号,该函数的使用方法与分配纹理对象编号类似。

2、用glBindBuffer绑定缓冲对象,可以用GL_ARRAY_BUFFER来绑定到顶点缓冲,也可以用GL_ELEMENT_ARRAY_BUFFER来绑定到索引缓冲。

3、用glBufferData指定缓冲中的数据,需要分别指定顶点缓冲的数据和索引缓冲的数据。注意该函数的最后一个参数,指明了该数据的用途,这个用途将帮助OpenGL决定如何进行优化。其中GL_STATIC_DRAW表示数据一旦确定,几乎不会更改,而且数据是用于绘制的。



成功使用以上三个函数后,很多本来接受指针的OpenGL函数在用法上就会发生变化。例如glColor3fv函数,本来接受一个指针,该指针指向三个连续的GLfloat类型的值,用于确定颜色。但因为现在绑定了GL_ARRAY_BUFFER,所以该函数接收一个偏移值offset,表示从已经绑定的缓冲中第offset个字节开始,取三个GLfloat类型的值,用于确定颜色。(注意:虽然偏移值是一个整数,但是你还是需要将它强制转换为glColor3fv所接受的指针类型,以确保编译能够顺利完成。)



在OpenGL 1.1版本中,提供了“顶点数组”的功能,可以把很多顶点数据放到数组中,然后通过调用很少的函数就完成绘制,而不必像OpenGL 1.0那样使用glBegin, glEnd以及大量的glColor*, glNormal*, glTexCoord*, glVertex*等函数。这些功能都是通过指针完成的。因此在绑定了顶点缓冲后,也可以用缓冲中的数据和指定偏移值的方式来代替原来的数组。数组是保存在内存中的,而缓冲数据则有可能是直接保存在显卡上,因此有望得到性能的优化。



本程序绘制一个旋转的立方体,按任意键可以开启/关闭旋转。

利用一个数组保存立方体中八个顶点的颜色和坐标,利用一个数组表示立方体六个面中每一面所包含的顶点的索引,然后利用顶点缓冲和顶点数组联合进行绘制。可以看到,除了第一次初始化外,以后只需要调用glDrawElements就可以完成绘制立方体所需要的所有动作。

程序有三种实现。当OpenGL版本为1.5或以上时,直接使用顶点缓冲对象;当OpenGL版本不足1.5,但支持ARB扩展形式的顶点缓冲对象时(此时也需要OpenGL 1.4版本),使用该形式的顶点缓冲对象;如果以上两者都不满足,则只好放弃使用顶点缓冲对象,而直接使用顶点数组。最后一种方式在性能上将会略低于前两种。

我测试用的是Intel的集成显卡,支持OpenGL 1.4以及ARB扩展形式的顶点缓冲对象。因此后两种方式我都测试并正确运行。至于第一种方式,就拜托有独立显卡(可能至少需要Geforce4 MX 440级别的显卡并安装最新驱动)的朋友去测试了。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:262306次
    • 积分:4376
    • 等级:
    • 排名:第6828名
    • 原创:133篇
    • 转载:96篇
    • 译文:32篇
    • 评论:47条
    最新评论