7 - 进阶缓冲操作

本文将讨论有关OpenGL缓冲的一些高级操作。

缓冲的高级操作

OpenGL的缓冲就是一个管理特定显存的对象,当我们用GL_ARRAY_BUFFERGL_ELEMENT_ARRAY_BUFFER等将其绑定时,就有了不同的意义。

glBufferSubData()

之前我们创建一块缓冲用的都是glBufferData(),可以对他进行填充,也能只分配内存后续再填充。这都是对一整块缓冲进行操作的,有没有只对缓冲中一小块区域进行操作的函数,有,就是glBufferSubData()

该函数需要一个缓冲目标,一个偏移量,数据大小和数据本身的地址:

glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); // 范围: [24, 24 + sizeof(data)]

gl(Un)mapBuffer()

当然,为了获得更多灵活性,可以用glMapBuffer()请求当前缓冲目标的指针,对其操作后使用glUnmapBuffer()解除当前指针的可用性,解除成功会返回GL_TRUE

一段示例如下:

// 获取当前缓冲目标
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取当前缓冲的指针
void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 对缓冲指向的内存进行操作
// ......
// 解绑指针
glUnmapBuffer(GL_ARRAY_BUFFER);

如果直接从文件中读数据,然后放到缓冲中,这两个函数会很有用,因为省的再开辟一段临时空间了。

分配顶点属性

之前使用glVertexAttribPointer()分配顶点属性时,都是按照顶点位置、法向量、纹理坐标等属性 交错存储 的。这样做不是很好,我们更希望这些属性可以被 批处理,从而优化程序效率。

可以用glBufferSubData()打包这些顶点属性,然后再用glVertexAttribPointer()来分配顶点属性:

float positions[] = { ... };
float normals[] = { ... };
float tex[] = { ... };

// 使用 glBufferSubData() 往缓冲中打包顶点属性
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);

// 分配顶点属性
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)));

glCopyBufferSubData()

可以通过glCopyBufferSubData()来将缓冲中部分内容复制到另一个缓冲中,它的函数原型如下:

void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size);

其中,各参数的释义如下:

  • readtarget:要读取的缓冲,可以将要读取的缓冲绑定至GL_COPY_READ_BUFFER然后使用该参数
  • writetarget:要写入的缓冲,可以将要读取的缓冲绑定至GL_COPY_WRITE_BUFFER然后使用该参数
  • read/writeoffset:偏移量
  • size:要复制内容的大小

例如要处理两个VBO的代码如下:

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));

参考资料

  • 高级数据 - LearnOpenGL CN (learnopengl-cn.github.io)