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级别的显卡并安装最新驱动)的朋友去测试了。
原文链接: https://www.cnblogs.com/Vulkan/archive/2012/03/21/7530306.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/44788
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!