将 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 版本不允许这样做)。
如果我有一个顶点着色器需要这个...
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 版本不允许这样做)。