为了避免反复向显卡传送相同的定点数据,绘制大量顶点数据时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);
}