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

参考资料