理解glVertexAttribPointer?

22
private int vbo;
private int ibo;

vbo = glGenBuffers();
ibo = glGenBuffers();

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, Util.createFlippedBuffer(vertices), GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, Util.createFlippedBuffer(indices), GL_STATIC_DRAW);




glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
//glEnableVertexAttribArray(2);

//glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);
glVertexAttribPointer(1, 2, GL_FLOAT, false, Vertex.SIZE * 4, 12);
//glVertexAttribPointer(2, 3, GL_FLOAT, false, Vertex.SIZE * 4, 20);

//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
glDrawElements(GL_TRIANGLES, size, GL_UNSIGNED_INT, 0);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
//glDisableVertexAttribArray(2);

顶点着色器代码看起来像这样

#version 330

layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;

out vec2 texCoord0;

uniform mat4 transform;

void main()
{
    gl_Position = transform * vec4(position, 1.0);
    texCoord0 = texCoord;
}

所以,以下是我的理解。glVertexAttribPointer的目的是定义顶点缓冲对象中数据的格式。因此,在vbo中它存储数据如下:

buffer.put(vertices[i].getPos().getX());
buffer.put(vertices[i].getPos().getY());
buffer.put(vertices[i].getPos().getZ());
buffer.put(vertices[i].getTexCoord().getX());
buffer.put(vertices[i].getTexCoord().getY());
buffer.put(vertices[i].getNormal().getX());
buffer.put(vertices[i].getNormal().getY());
buffer.put(vertices[i].getNormal().getZ());
所以,我们有两行glVertexAttribPointer,因为在顶点着色器中定义了两个变量。基本上我们正在定义这两个变量指向什么。因此,第一个glVertexAttribPointer定义了第一个变量“position”是具有三个坐标的顶点,每个坐标都是浮点数。第二个glVertexAttribPointer定义了第二个变量“texCoord”是一对纹理坐标,每个坐标都是浮点数。所以,如果我到目前为止的理解是正确的,那么我认为我们首先需要绑定顶点缓冲对象,但即使注释掉这一行也可以。
glBindBuffer(GL_ARRAY_BUFFER, vbo);

它仍然工作。我感到困惑。由于存在两个VBO,它是如何知道我们正在谈论哪个缓冲对象的呢?


1
没有两个VBO。有一个VBO和一个IBO。它们都可以(而且必须)同时保持绑定状态。 - fintelia
那么这是否意味着如果有两个VBO,则我们需要显式绑定它,但由于只有一个,因此它默认绑定了? - user3256520
3
实际上不需要。在调用gl...Pointer函数之后,您可以安全地解绑VBO,与调用该函数时绑定的VBO的关联不会丢失。 - datenwolf
1个回答

33

@datenwolf 已经在上面的评论中涵盖了关键方面。再详细解释一下:

在调用glDrawElements()之前,您不需要再次绑定GL_ARRAY_BUFFER。重要的是,在为该属性调用glVertexAttribPointer()时绑定要从中获取给定属性的缓冲区。

最好的方法是将此视为:

glVertexAttribPointer(0, 3, GL_FLOAT, false, Vertex.SIZE * 4, 0);

在调用时,您指定了所有状态所需的信息以告诉OpenGL从哪里获取属性0的数据(第一个参数)以及如何读取它。其中大部分状态都是由参数直接给出的:

  • 它有3个组件
  • 这些组件是浮点值
  • 顶点以20字节的跨度读取...
  • ... 并且从缓冲区的第0个字节开始

但是,在调用时还有另一个隐含的状态被存储到属性0中:

  • 数据从当前绑定到GL_ARRAY_BUFFER的缓冲区中读取

换句话说,与每个属性相关联的状态包括属性数据源自的缓冲区的ID。这可以是多个/所有属性的相同缓冲区,也可以是每个属性的不同缓冲区。

请注意,对于GL_ELEMENT_ARRAY_BUFFER并非如此。那个需要在glDrawElements()调用时绑定。虽然这似乎有些不一致,但这是必要的,因为没有类似于索引数组的glVertexAttribPointer()。API本可以定义具有此类调用,但是......它没有。原因很可能是根本没有必要,因为一次绘制调用只能使用一个索引数组,而可以使用多个顶点缓冲区。


简而言之,我已经绑定了这两个缓冲区。 glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); 因此我不需要重新绑定,对吗? - user3256520
1
当然,您不需要重新绑定已经绑定的缓冲区。我在回答中试图涵盖的是哪些调用需要绑定哪些缓冲区。 - Reto Koradi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接