VAO VBO IBO大乱炖

来源:互联网 发布:没文凭自学编程找工作 编辑:程序博客网 时间:2024/05/14 06:08

 最近对程序中绘制卡顿的问题忍无可忍,终于决定下手处理了。程序涉及的绘制比较多,除了点、线、三角形、多边形、圆柱体之外,还有自组格式模型。开始想全部采用显示列表优化,毕竟效率最高,虽然显示列表存在编译之后不能更改的缺点,但是程序中更改模型的情况不多,更改每个类别的模型后重新加载该类别的显示列表就行了。

1.显示列表+顶点数组

  显示列表的使用就不赘述了。主要想说一下配合顶点数组使用显示列表,可以带来更大的性能提升。对于三角网格和基于顶点的自组格式模型来说,摒弃一个个三角形画吧,效果绝对令你满意!当然,这么做的前提应该是顶点vertex在不同三角形中的属性相同(法线、颜色……)

list = glGenLists(1);
glNewList(list);

……
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);

glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);//纹理

 

glTexCoordPointer(2,GL_FLOAT,0,textures);

glColorPointer(3,GL_FLOAT,0,colors);//颜色 nTri*3

glNormalPointer(GL_FLOAT,0,normals);//法线 nTri*3
glVertexPointer(3,GL_FLOAT,0,vertexs);//顶点 nVer
glDrawElements(GL_TRIANGLES,nTri*3,GL_UNSIGNED_INT,iIndex);//索引 nTri*3

glEndList();

2.VBO(vertex buffer object)+VAO(vertex array object)

  另一个选择是采用VBO进行绘制。VBO的好处是采用将数据提交到高速缓存的同时还保留数据的存取入口,可以直接获取并更新数据。VBO有两种方式,一种是使用glEnableClientState进行绘制,另一种就是组合VAO进行使用,也就是将对应不同属性的多组VBO存储到同一个VAO中(在渲染的时候可以少写很多代码)。

  关于两种方式的取舍在这篇文章里面有介绍(http://www.zwqxin.com/archives/opengl/vao-and-vbo-stuff.html),这里我使用了第二种方式,也就是VBO+VAO进行使用。

GLuint vao;
GLuint vbo;

……

//初始化代码
glGenVertexArrays(1,&vao);
glBindVertexArray(vao);//当前VAO 到glBindVertexArray(NULL)之间的vbo等都成为该vao的属性  
//
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER,12 * 3 * sizeof(float),vertexs,GL_STATIC_DRAW);//此处点坐标按照每个四边形四个点进行存入
glEnableVertexAttribArray(0); //坐标 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);
//
glBindVertexArray(NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);

……

//渲染代码

glBindVertexArray(vbo); 
glDrawArrays(GL_QUADS,0,4);

glBindVertexArray(vbo); 

  之后突然想到,VA的使用中,共用顶点的索引方式index会比直接每个多边形顶点暴力存储效率高很多。所以查了下,果然有关于索引的用法:

   ---IBO(http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/)

   其实就是VBO的方式运用索引而已,GL_ELEMENT_ARRAY_BUFFER。经过修改,代码就变成了下面的样式:

   

GLuint vao;

 

……

glGenVertexArrays(1,&vao);
glBindVertexArray(vao);//当前VAO 到glBindVertexArray(NULL)之间的vbo等都成为该vao的属性  
//
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER,nVert * 3 * sizeof(float),vertexs,GL_STATIC_DRAW);
glEnableVertexAttribArray(0); //坐标 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);
//
GLuint colorVbo;//
glGenBuffers(1, &colorVbo);
glBindBuffer(GL_ARRAY_BUFFER, colorVbo);
glBufferData(GL_ARRAY_BUFFER,nVert * 3 * sizeof(float),colors,GL_STATIC_DRAW);
glEnableVertexAttribArray(1); //颜色
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);
//
GLuint normalVbo;//
glGenBuffers(1, &normalVbo);
glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
glBufferData(GL_ARRAY_BUFFER,nVert * 3 * sizeof(float),normals,GL_STATIC_DRAW);
glEnableVertexAttribArray(2); //法线
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);
//
GLuint ibo;
glGenBuffers(1, &ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,nElem * 4 * sizeof(short), index,GL_STATIC_DRAW);//由于是绘制六棱柱侧面,nElem = nVert/2
//
glBindVertexArray(NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL);
glBindBuffer(GL_ARRAY_BUFFER,NULL);

 渲染部分代码稍作改变

 

glBindVertexArray(vao); 
glDrawElements(GL_QUADS,24, GL_UNSIGNED_SHORT, 0); 
glBindVertexArray(NULL);

 

3.目前存在的问题

  (1)由于我的程序仍在opengl 2.0下写的,并没有采用shader和GLSL,要做的事情实在太多,明知道需要学习,但还是无法在技术上投入太多时间。

  (2)在上面VAO+VBO+IBO版的代码中遇到了一个问题:颜色没有设置成功(法线没问题),不知为何,仍在寻找中。如果有高手看到烦请赐教。

  (3)关于VBO的更新:目前还没测试更新的效率。GL_STATIC_DRAW之后两种更新方式(复制和直接给出内存映射地址)貌似都需要重新设置/更新数据,还未测试GL_DYNAMIC_DRAW的使用效率。

  (4)对其后台机制还是模棱两可。还是那句话,烦请高手赐教!也欢迎新手讨论!