第四章 高级OpenGL
4.6 数据与缓冲
在OpenGL中使用缓冲来储存数据,本节将讨论不同的缓冲函数,以及如何使用纹理对象来储存大量的数据。缓冲是用来管理特定内存块的对象,将它绑定到一个缓冲目标后,才赋予了其意义。例如,将缓冲绑定到GL_ARRAY_BUFFER
或GL_ELEMENT_ARRAY_BUFFER
等缓冲目标上,OpenGL根据目标的不同,以不同的方式处理缓冲。
4.6.1 缓冲函数
1、之前一直调用glBufferData
函数来填充缓冲对象所管理的内存,这个函数会分配一块内存,并将数据添加到这块内存中。如果data
参数设置为NULL
,将只会分配内存,但不进行填充。
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);
2、可使用glBufferSubData
函数填充缓冲的特定区域。参数为:缓冲目标、偏移量、数据的大小和数据本身。偏移量指定从何处开始填充这个缓冲,因此可以插入或者更新缓冲内存的某一部分,但缓冲需要有足够的已分配内存,所以对一个缓冲调用glBufferSubData
之前必须要先调用glBufferData
。
glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 范围: [24, 24 + sizeof(data)]
3、将数据导入缓冲的另外一种方法是:通过调用glMapBuffer
函数,返回当前绑定缓冲的内存指针,直接将数据复制到缓冲当中。该函数用于直接映射数据到缓冲,而不事先将其存储到临时内存中,比如从文件中读取数据,并直接将它们复制到缓冲内存中。
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));
//不再需要这个指针了
glUnmapBuffer(GL_ARRAY_BUFFER);
4.6.2 分批顶点属性
1、之前,通过glVertexAttribPointer
函数指定顶点数组缓冲内容的属性布局。在顶点数组缓冲中,对属性进行了交错处理,将每一个顶点的位置、法线和纹理坐标紧密放置在一起。
2、另一种方式是:将每一种属性类型的向量数据打包为一个大的区块。与交错布局123123123123
不同,采用分批的方式111122223333
。
3、从文件中加载顶点数据时,通常获取到的是位置数组、法线数组和纹理坐标数组。使用glBufferSubData
函数进行缓冲填充:
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);
更新顶点属性指针,stride参数等于顶点属性的大小
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) &