1. 流程
1.1 填充缓冲
除了用缓冲来储存数据,我们还可以有更多的玩法
首先,用glBufferData往VBO里填充数据的时候,我们可以把data参数设置为NULL,让它被分配为空的内存,然后我们可以利用glBufferSubData在自己想要的位置插入数据:
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 范围: [24, 24 + sizeof(data)]
如上,我们在分配的内存里从第24位开始填充数据
除此之外,我们还可以请求缓冲内存的指针,直接将数据复制到缓冲当中
float data[] = {
0.5f, 1.0f, -0.35f
...
};
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取指针
void *ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 复制数据到内存
memcpy(ptr, data, sizeof(data));
// 记得告诉OpenGL我们不再需要这个指针了
glUnmapBuffer(GL_ARRAY_BUFFER);
如上,通过调用glMapBuffer函数,OpenGL会返回当前绑定缓冲的内存指针,供我们操作
当我们使用glUnmapBuffer函数,告诉OpenGL我们已经完成指针操作之后,OpenGL就会知道你已经完成了。在解除映射(Unmapping)之后,指针将会不再可用,并且如果OpenGL能够成功将你的数据映射到缓冲中,这个函数将会返回GL_TRUE。
如果要直接映射数据到缓冲,而不事先将其存储到临时内存中,glMapBuffer这个函数会很有用。比如说,你可以从文件中读取数据,并直接将它们复制到缓冲内存中。
1.2 分批顶点属性
之前通过使用glVertexAttribPointer,我们能够指定顶点数组缓冲内容的属性布局,比如可以将每一个顶点的位置、法线和/或纹理坐标紧密放置在一起,而现在,我们还可以通过上面填充缓冲的方式实现:
float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };
// 填充缓冲
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(positions), &positions);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions), sizeof(normals), &normals);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(positions) + sizeof(normals), sizeof(tex), &tex);
这样子我们就能直接将属性数组作为一个整体传递给缓冲,而不需要事先处理它们了。我们仍可以将它们合并为一个大的数组,再使用glBufferData来填充缓冲,但对于这种工作,使用glBufferSubData会更合适一点。
同样的,缓冲布局更改后,顶点属性也要更改:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(sizeof(positions)));
glVertexAttribPointer(
2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(sizeof(positions) + sizeof(normals)));
1.3 复制缓冲
当你的缓冲已经填充好数据之后,你可能会想与其它的缓冲共享其中的数据,或者想要将缓冲的内容复制到另一个缓冲当中。glCopyBufferSubData能够让我们相对容易地从一个缓冲中复制数据到另一个缓冲中。
float vertexData[] = { ... };
glBindBuffer(GL_COPY_READ_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
如上,我们将vbo1绑定为GL_COPY_READ_BUFFER,也就是用来读的缓冲,将vbo2绑定为GL_COPY_WRITE_BUFFER,也就是用来写的缓冲,然后在glCopyBufferSubData里我们从vbo1读取数据复制到vbo2,所以可以看出第一个参数和第二个参数是指的源和目的缓冲的缓冲类型,第三个和第四个参数是分别指的源和目的缓冲读取存储数据的起始位置,第五个参数是指的复制数据的大小
可见并不是缓冲类型是GL_COPY_READ_BUFFER,它就只能作为源缓冲了,因此,我们也可以只将writetarget缓冲绑定为新的缓冲目标类型之一:
float vertexData[] = { ... };
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glBindBuffer(GL_COPY_WRITE_BUFFER, vbo2);
glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));