需要多次调用 glVertexAttribPointer 吗?

multiple calls to glVertexAttribPointer necessary?

while following the lighting chapter in the learnopengl series,作者在创建多个VAOs(Vertex Array Objects)时提供了这样的代码:

    unsigned int VBO, cubeVAO;
    glGenVertexArrays(1, &cubeVAO);
    glGenBuffers(1, &VBO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindVertexArray(cubeVAO);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //#this
    glEnableVertexAttribArray(0);

    // second, configure the light's VAO (VBO stays the same; the vertices are the same for the light object which is also a 3D cube)
    unsigned int lightVAO;
    glGenVertexArrays(1, &lightVAO);
    glBindVertexArray(lightVAO);

    // we only need to bind to the VBO (to link it with glVertexAttribPointer), no need to fill it; the VBO's data already contains all we need (it's already bound, but we do it again for educational purposes)
    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //#this
    glEnableVertexAttribArray(0);

这里我们使用相同的 VBO 和多个 VAO,但对 glVertexAttribPointer 的调用是使用相同的参数完成两次。 Earlier in this lesson 他提到:

Each vertex attribute takes its data from memory managed by a VBO and which VBO it takes its data from (you can have multiple VBOs) is determined by the VBO currently bound to GL_ARRAY_BUFFER when calling glVertexAttribPointer. Since the previously defined VBO is still bound before calling glVertexAttribPointer vertex attribute 0 is now associated with its vertex data.

所以,这是否意味着这两个调用是多余的,或者这是必要的,如果不这样做会导致问题吗?

so, doesn't that mean that these two calls are redundant, or is this necessary and will cause problems down the road if not done?

没有。看这里的操作顺序。

// the buffer bound to GL_ARRAY_BUFFER is VBO, 
// from here, until the end of the code in this block
glBindBuffer(GL_ARRAY_BUFFER, VBO);

// start modifying the cubeVAO
// cubeVAO currently knows NOTHING about which attributes are needed.
glBindVertexArray(cubeVAO);

// set up info about vertex attribute 0, within cubeVAO. 
// cubeVAO now knows about 1 attribute, index == 0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //#this
glEnableVertexAttribArray(0);


// now start setting up the lightVAO. 
// note that at this point, VBO is still bound to GL_ARRAY_BUFFER
glBindVertexArray(lightVAO);

// set up info about vertex attribute 0, within lightVAO. 
// lightVAO now knows about 1 attribute, index == 0
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); 
glEnableVertexAttribArray(0);

如果您省略对 glVertexAttribPointer 和 glEnableVertexAttribArray 的第二次调用,lightVAO 将包含恰好为零顶点属性的绑定。这意味着您的顶点着色器中的任何属性都不会接收任何数据。

在您的原始代码中,不需要第二次调用 glBindBuffer。这么说也是对的,因为cubeVAO和lightVAO只有一个属性,是从同一个buffer中读取的;在这里可以简单地使用一个 VAO。

\编辑

最好从槽的角度来考虑它,而不是实例。您的 GPU 将支持固定数量的顶点属性(在 GL_MAX_VERTEX_ATTRIBS 上执行 glGet 以找出有多少)。所以唯一支持的索引是:0 -> (GL_MAX_VERTEX_ATTRIBS-1),所以说 'a new instance is created' 并不准确(因为这意味着动态分配)。该行为更类似于:

// struct to store info about a vertex attribute
struct VertexAttribute
{
  bool enabled = false; //< on or off?
  int size;
  GLenum dataType;
  bool normalise;
  int stride;
  size_t offset;
  GLuint buffer; //< which buffer was bound to GL_ARRAY_BUFFER
};

// the VAO just stores the current state of the vertex bindings 
struct VAO
{
  VertexAttribute vertexAttribs[GL_MAX_VERTEX_ATTRIBS];

  void glVertexAttribPointer(
     int index, int size, GLenum type, 
     bool normalise, int stride, size_t offset)
  {
    vertexAttribs[index].size = size;
    vertexAttribs[index].dataType = type;
    vertexAttribs[index].normalise = normalise;
    vertexAttribs[index].stride = stride;
    vertexAttribs[index].offset = offset;

    // grab buffer
    vertexAttribs[index].buffer = glGet(GL_ARRAY_BUFFER_BINDING);
  }

  void glDisableVertexAttribArray(uint32_t index)
  {
    vertexAttribs[index].enabled = false;
  }

  void glEnableVertexAttribArray(uint32_t index)
  {
    vertexAttribs[index].enabled = true;
  }
};