为了避免反复向显卡传送相同的定点数据,绘制大量顶点数据时OpenGL下可以使用缓存对象(Buffer Object)来将数据上传到显卡。
准备数据
我们的显示数据为一正方体,如下所示
顶点数据结构为颜色(RGBA)法线(xyz)坐标(xyz)
顶点数据存储在vertices, 定点的索引数据存储在indices, 同时还需要缓存对象的句柄vertexBuffer和indexBuffer
struct CUSTOM_VERTEX { float r, g, b, a; float nx, ny, nz; float x, y, z; }; CUSTOM_VERTEX vertices[] = { { 1.0f, 0.0f, 0.0f, 1.0f, -0.577f, 0.577f, 0.577f, -1.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, -0.577f, -0.577f, 0.577f, -1.0f, -1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, 0.577f, 0.577f, 0.577f, 1.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, 0.577f, -0.577f, 0.577f, 1.0f, -1.0f, 1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, 0.577f, 0.577f, -0.577f, 1.0f, 1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, 0.577f, -0.577f, -0.577f, 1.0f, -1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, -0.577f, 0.577f, -0.577f, -1.0f, 1.0f, -1.0f }, { 1.0f, 0.0f, 0.0f, 1.0f, -0.577f, -0.577f, -0.577f, -1.0f, -1.0f, -1.0f }, }; GLuint indices[] = { 0, 2, 3, 1, 2, 4, 5, 3, 4, 6, 7, 5, 6, 0, 1, 7, 6, 4, 2, 0, 1, 3, 5, 7, }; int num_indices = sizeof(indices) / sizeof(indices[0]); int num_vertices = sizeof(vertices) / sizeof(vertices[0]); GLuint vertexBuffer; GLuint indexBuffer;初始化
glGenBuffers创建缓冲对象,句柄(创建对象的标示符)存储在vertexBuffer,本例中创建对象数量为1
glBindBuffer激活缓冲区,指定当前缓冲对象,从而使接下的操作都是对该对象进行的。GL_ARRAY_BUFFER表示顶点数据,GL_ELEMENT_ARRAY_BUFFER表示索引数据。
该函数有三个功能:
1.buffer(非0)为首次使用,创建新缓冲对象
2.buffer为已创建对象则激活该对象
3.buffer为0则停止使用该对象
glBufferData为缓存对象分配数据空间,然后将数据从内存复制到缓冲对象,GL_STATIC_DRAW表示数据只复制一次,可多次用来绘图。
// 顶点缓存 glGenBuffers(1, &vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBufferData(GL_ARRAY_BUFFER, num_vertices * sizeof(CUSTOM_VERTEX), vertices, GL_STATIC_DRAW); // 索引缓存 glGenBuffers(1, &indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_indices * 4, indices, GL_STATIC_DRAW); // 解除绑定 glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// 激活缓存对象 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); // 指定数组格式 glInterleavedArrays(GL_C4F_N3F_V3F, 0, NULL); // 开始绘制 glDrawElements(GL_QUADS, num_indices, GL_UNSIGNED_INT, NULL); // 绘制完成,解除绑定 // 转载请注明http://blog.csdn.net/boksic // 如有疑问欢迎留言 glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
效果如下
缓存数据的更新
接下来时这更新顶点数据,可以使用glMapBuffer来获取(该方法适合大量更新的情况,部分更新时应使用glBufferSubData等其他函数)
本例中更新所有顶点的坐标让其随时间作正弦运动
t += 0.0001; GLfloat* data; data = (GLfloat*)glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE); if (data != (GLfloat*)NULL){ for (int i = 0; i < num_vertices; i++){ data[10 * i + 8] += 0.001*cos(t); } glUnmapBuffer(GL_ARRAY_BUFFER); }