【问题标题】:Passing VBO to shaders with different layouts将 VBO 传递给具有不同布局的着色器
【发布时间】:2020-03-13 02:32:58
【问题描述】:

如果我有一个期望这个的顶点着色器......

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 吗?如果我的顶点数据是交错的,那么我是否也必须创建一个新的顶点数据缓冲区?

【问题讨论】:

  • “新的 VBO”是什么意思?您是在谈论复制数据还是使用不同的顶点格式?此外,如果您想与不同的着色器共享顶点格式,最好让每个属性的位置代表相关数据的类型。例如,您可以将位置 1 指定为始终表示骨骼重量。
  • 谢谢尼科尔。感谢您的回答。
  • 这是评论,不是答案。您应该提供所要求的信息(在您的问题中)。
  • 对不起。我是新手。 “新 VBO”是指将部分交错数据复制到新的 VBO 中。 (我也非常感谢您建议将某些位置分配为始终表示相同的意思)

标签: opengl opengl-es glsl vertex-shader vertex-buffer


【解决方案1】:

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

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

一些用于存储 VAO 名称的全局变量

GLuint g_vao;

然后创建它(对于您在着色器中定义的数据布局)

// 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(或一组 VBO,如果从多个数据中提取数据)创建 VAO,因为属性绑定到确切的指定该属性时绑定的缓冲区。

OpenGL 4.3:glVertexAttribFormat

这个版本很像我上面介绍的那个。与其将 VAO 传递给函数调用,不如先调用 glBindVertexArray(如果搜索上述方法的文档,没有 VAO 参数的方法只使用当前绑定的 VAO)

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

OpenGL 4.5:glVertexArrayAttribFormat

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

【讨论】:

  • 谢谢。好东西!对于选项 1,您可以只使用 glVertexAttribPointer(location, attributeSize, type, isNormalized, stride, (void*)offset);相反,偏移量是隔行扫描 vbo 缓冲区中的位置?
  • 是的,您可以,但 VBO 现在将绑定到 VAO。要重新绑定到不同的 VBO,您几乎必须重新创建 VAO。我在上面添加了一些额外的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
  • 2017-12-27
  • 2015-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-27
相关资源
最近更新 更多