将 VBO 传递给具有不同布局的着色器

Passing VBO to shaders with different layouts

如果我有一个顶点着色器需要这个...

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec4 aBoneWeights; 
layout(location = 2) in vec4 aBoneIndices;

如何传递一个已经为每个顶点组织的 VBO

Position(vec3) | Color(vec3) | UV(vec2) | BoneWeight(vec4) | BoneIndex(vec4)

我必须制作一个新的 VBO 吗?如果我的顶点数据是交错的,那么我是否也必须创建一个新的顶点数据缓冲区?

选项 1:为每个着色器创建不同的 VAO

VAO 定义了来自着色器属性的映射(例如,从 VBO 中的此内存位置读取 vec3,步幅为 N 字节,并将其映射到绑定到位置 X 的属性)。

一些全局存储VAO名称

GLuint g_vao;

然后创建它(对于你在shader中定义的数据布局):

// create the VAO 
glCreateVertexArrays(1, &g_vao);


// set up: layout(location = 0) in vec3 aPos;
glEnableVertexArrayAttrib(g_vao, 0);  //< turn on attribute bound to location 0

// tell OpenGL that attribute 0 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  0,     //< the attribute index (location = 0)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  0,        //< the attribute index
  3,        //< there are 3 values xyz
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  0);       //< the offset (in bytes) from the start of the buffer where the data starts



// set up: layout(location = 1) in vec4 aBoneWeights
glEnableVertexArrayAttrib(g_vao, 1);  //< turn on attribute bound to location 0

// tell OpenGL that attribute 1 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  1,     //< the attribute index (location = 1)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  1,        //< the attribute index
  4,        //< there are 4 values
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  sizeof(float) * 8); //< the offset (in bytes) from the start of the buffer where the data starts



// set up: layout(location = 2) in vec4 aBoneIndices;
glEnableVertexArrayAttrib(g_vao, 2);  //< turn on attribute bound to location 2

// tell OpenGL that attribute 2 should be read from buffer 0
glVertexArrayAttribBinding(
  g_vao, //< the VAO
  2,     //< the attribute index (location = 2)
  0);    //< the vertex buffer slot (start from zero usually)

// tell openGL where within the buffer the data exists
glVertexArrayAttribFormat(
  g_vao,    //< the VAO
  2,        //< the attribute index
  4,        //< there are 4 values xyz
  GL_FLOAT, //< all of type float
  GL_FALSE, //< do not normalise the vectors
  sizeof(float) * 12);       //< the offset (in bytes) from the start of the buffer where the data starts

但是,我认为您的着色器定义对于属性 2 是错误的(因为您必须将骨骼索引作为浮点数据传递,这对我来说是非常错误的!) .

我以为你会想要整数而不是浮点数:

layout(location = 2) in ivec4 aBoneIndices;

但是当绑定到整数时,您需要使用 glVertexArrayAttribIFormat 而不是 glVertexArrayAttribFormat:

glVertexArrayAttribIFormat(
  g_vao,    //< the VAO
  2,        //< the attribute index
  4,        //< there are 4 indices
  GL_UNSIGNED_INT, //< all of type uint32
  sizeof(float) * 12);   

完成所有这些之后,您需要将顶点缓冲区绑定到您在上面使用的顶点槽零...

glVertexArrayVertexBuffer(
  g_vao, //< the VAO
  0,     //< the vertex buffer slot
  0,     //< offset (in bytes) into the buffer
  sizeof(float) * 16); //< num bytes between each element

选项2:只使用相同的VAO和相同的VBO

只需对索引进行编码使其具有特定含义,然后您就可以始终使用相同的 VAO。

layout(location = 0) in vec3 aPos;
//layout(location = 1) in vec4 aCol;  //< not used in this shader
//layout(location = 2) in vec4 aUv;   //< not used in this shader
layout(location = 3) in vec4 aBoneWeights; 
layout(location = 4) in vec4 aBoneIndices;

/编辑 在回答您的问题时,这在很大程度上取决于您使用的 OpenGL 版本。我在此处发布的答案使用了 OpenGL4.5 中最新的直接状态访问 (DSA) 扩展。如果您可以利用它们,我强烈建议您这样做。

OpenGL 3.0:glVertexAttribPointer

是的,这会奏效。但是,它是一种与 OpenGL 的 bind 范例紧密相关的机制。当您调用 glVertexAttribPointer (即您将执行:glBindBuffer();glEnableVertexAttribArray();glVertexAttribPointer();) 时,每个属性都有效地绑定到缓冲区。

这个问题是它有点让你无法为每个 VBO 创建一个 VAO (或者一组 VBO,如果从多个 VBO 中提取数据),因为属性绑定到指定该属性时绑定的确切缓冲区。

OpenGL 4.3:glVertexAttribFormat

这个版本与我在上面介绍的版本非常相似。不是将 VAO 传递到函数调用中,而是首先调用 glBindVertexArray (如果您搜索有关上述方法的文档,没有 VAO 参数的方法只使用当前绑定的 VAO).

这种方法相对于旧方法 API 的优势在于,将 VAO 绑定到另一个 VBO[即您可以将 VAO 与 GLSL 程序相关联,而不是为每个 VBO/Program 对] 设置一个 VAO - 只需为每个 VBO 调用 glBindVertexBuffer,它会很好地工作。

OpenGL 4.5:glVertexArrayAttribFormat

在长 运行 中,这是迄今为止最容易使用的 API 版本。主要优点是您无需担心当前绑定了哪个 VAO(因为您将其作为参数传入)。这有很多优点,因为您不再关心绑定了哪个 VAO,而且它还打开了从不同线程修改 OpenGL 对象的大门(旧的 API 版本不允许这样做)。