不深入理解OpenGL: UBO (Uniform Block Object)

最近刚刚开始研究图形方面的东西,如果理解有误请务必指正。在OpenGL 4.5已经有更好的解法了,奈何我辣鸡I卡只支持4.4。

UBO是OpenGL 3.1 Core中引入的概念。通过到缓存的通信,修改Uniform变量(=运行时常量)造成的开销更低,不同的Shader间还可以共享同一UBO的内容。具体就不扯那么多了,有兴趣可以阅读ARB原文了解更多。

因为OpenGL的状态机模型,有的时候调用相关API会发生意料之外的结果。一般也就是画不出东西,再惨就是报错了。这里记录一下UBO的缓冲区生成与绑定流程:

GLuint buf;
glGenBuffers(1, &buf); // 创建缓存,此时仅仅创建了缓存句柄(name、handle、id,就那个意思),对象并没有被创建。
auto index = glGetUniformBlockIndex(prog, "UBOName"); // 通过UBO名查询索引。
glUniformBlockBinding(prog, index, bindingPoint); // 绑定着色器程序$prog的UBO到$bindingPoint。bindingPoint不和同一程序其他UBO重复即可。

glBindBuffer(GL_UNIFORM_BUFFER, buf); // 在首次绑定时会通过句柄构造对象。
glBufferData(GL_UNIFORM_BUFFER, GetUniformBlockSize(index), nullptr, GL_STATIC_DRAW); // 注意显存中内容是未定义的,在指定数据前不可使用。
glBindBuffer(GL_UNIFORM_BUFFER, 0); // 为了安全解除绑定。

这里通过buf就可以修改相应UBO的内容了。

绘图时再完成另一半的绑定:

glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, buf); // 绑定缓存$buf到$bindingPoint。也可以使用glBindBufferRange()。

需要注意的地方:

  1. glBindBufferBase()被调用时虽然也会将buf绑定到GL_UNIFORM_BUFFER,但在缓存对象没有被构造的时候调用glBindBuffer{Base, Range}会造成GL_INVALID_VALUE错误,参见这里。所以在构建UBO缓存的时候并没有使用这两个方法。
  2. 从UBO到Binding Point的绑定是对着色器程序的操作;而从缓存到Binding Point的绑定是对OpenGL状态机进行的操作。所以,在每次draw的时候不需要重新定义UBO到Binding Point的绑定,但需要重新定义对缓存的绑定。参见这里
posted @ 2017-05-29 23:17  PENGUINLIONG  阅读(3194)  评论(0编辑  收藏  举报