OpenGL 4.5如何绑定VBO



  OpenGL 4.3 的更新中,使用 glVertexAttribFormat 和 glBindVertexBuffer 取代 glVertexAttribPointer 来将 VBO 绑定到 VAO。由于老是忘记写法,故记录下来。本文使用 OpenGL 4.5 新增的 DSA 函数,最后会贴出与 4.3 函数的关系表,它们是一一对应的。

glVertexAttribPointer 的缺陷

  glVertexAttribPointer 函数有两个缺陷
  第一个缺陷是它依赖于 GL_ARRAY_BUFFER 的内容。调用 glVertexAttribPointer 前,必须绑定 vbo,然后将特定被绑定 vbo 的索引、格式信息复制到 vao 中。这是一种非常笨拙的方式,因为这样 vao 和特定 vbo 处于绑死的关系,一旦对这个特定 vbo 有任何修改,都要重新绑定 vbo 的信息到 vao 中(根据实现有所不同),这一点上很容易产生 bug,云风的博客也说明过这个问题。
  不仅如此,glVertexAttribPointer 最后一个参数使用指针而不是整数来表示偏移量。使用时都要加一个丑陋的转换,相信很多人都碰到过下述的写法。

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), static_cast<void*>(6 * sizeof(float)));

  第二个缺点是它将逻辑上分离的两个操作合在一起:获取数据、组织数据。这也带来性能的浪费。如果只要改变组织数据的方式,比如改变偏移量,也必须一次性指定 buffer 信息和组织格式;切换绑定的 buffer 也是如此。
  话虽如此,即使在写 OpenGL 4.5 程序的时候,这两个操作几乎是永远伴随在一起的(大笑)。这也是早期定义 glVertexAttribPointer 的原因,更先进的 OpenGL 将它们区分开,给予程序员选择的自由。
  

获取数据

  OpenGL 4.5 可以使用 glVertexArrayVertexBuffer 来将 vbo 绑定到 vao 独有的绑定点,与 ubo 的绑定点相似,但相互不干扰。然后使用 glVertexArrayAttribBinding 将 glsl 中的 attrib 变量绑定到同一个绑定点即可。

void glVertexArrayVertexBuffer(GLuint vaobj, // vao 系数
    GLuint bindingindex,                    // 绑定点
    GLuint buffer,                          // 需要绑定的 vbo
    GLintptr offset,                        // vbo 在 buffer 内的偏移字节数
    GLsizei stride);                        // 每个 vertex 的字节数(pos,normal 等表示一个 vertex)

void glVertexArrayAttribBinding(GLuint vaobj, // vao 系数
    GLuint attribindex,                      // attrib 位置(可以在 glsl 中指定)
    GLuint bindingindex);                    // 绑定点

组织数据

  glVertexArrayAttribFormat 被用来指定 attrib 的组织形式。它是绑定到 attrib 上的,而不是 bindingindex 上。

void glVertexAttribFormat(GLuint attribindex, // attrib 位置
    GLint size,                              // 数据分量的个数,RGB 为 3
    GLenum type,                             // 每个分量的格式
    GLboolean normalized,                    // 整形是否已经可以映射到 0-1
    GLuint relativeoffset);                  // 不同变量的相对偏移,比如 pos 和 uv

  比如,对于下面的例子,位置、颜色和纹理坐标存储在一个 vbo 中。

struct Vertex
{
    GLfloat pos[3];
    GLubyte color[4];
    GLushort texCoord[2];
};

  就可以使用下面的方式组织数据。

glVertexAttribFormat(0, ..., offsetof(Vertex, pos)); //AKA: 0
glVertexAttribFormat(1, ..., offsetof(Vertex, color)); //Probably 12
glVertexAttribFormat(2, ..., offsetof(Vertex, texCoord)); //Probably 16

  

比较

  既然上述函数是拆分后的结果,就可以用新函数来表示老函数,如下所示。

void glVertexAttrib*Pointer(GLuint index​, GLint size​, GLenum type​, {GLboolean normalized​,} GLsizei stride​, const GLvoid * pointer​)
{
  glVertexAttrib*Format(index, size, type, {normalized,} 0);
  glVertexAttribBinding(index, index);

  GLuint buffer;
  glGetIntegerv(GL_ARRAY_BUFFER_BINDING, buffer);
  if(buffer == 0)
    glErrorOut(GL_INVALID_OPERATION); //Give an error.

  if(stride == 0)
    stride = CalcStride(size, type);

  GLintptr offset = reinterpret_cast<GLintptr>(pointer);
  glBindVertexBuffer(index, buffer, offset, stride);
}

附:函数对照表

OpenGL 4.5 OpenGL 4.3
glVertexArrayVertexBuffer glBindVertexBuffer
glVertexArrayAttribBinding glVertexAttribBinding
glVertexArrayAttribFormat glVertexAttribFormat


About the Author

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注