What Is the Role of Glbindvertexarrays VS Glbindbuffer and What Is Their Relationship

What is the role of glBindVertexArrays vs glBindBuffer and what is their relationship?

From a low-level perspective, you can think of an array as having two parts to it:

  • Information about the size, shape, and type of the array (e.g., 32-bit floating point numbers, containing rows of vectors with four elements each).

  • The array data, which is little more than a big blob of bytes.

Even though the low-level concept has mostly stayed the same, the way you specify arrays has changed several times over the years.

OpenGL 3.0 / ARB_vertex_array_object

This is the way you probably should be doing things today. It is very rare to find people who can't run OpenGL 3.x and yet still have money to spend on your software.

A buffer object in OpenGL is a big blob of bits. Think of the "active" buffer as just a global variable, and there are a bunch of functions which use the active buffer instead of using a parameter. These global state variables are the ugly side of OpenGL (prior to
direct state access, which is covered below).

GLuint buffer;

// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);

// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
// opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Now, your typical vertex shader takes vertexes as input, not a big blob of bits. So you need to specify how the blob of bits (the buffer) is decoded into vertexes. That is the job of the array. Likewise, there is an "active" array which you can think of as just a global variable:

GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
// type = GL_FLOAT,
// size = 4,
// data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);

OpenGL 2.0 (the old way)

In OpenGL 2.x, there weren't vertex arrays and the data was just global. You still had to call glVertexAttribPointer() and glEnableVertexAttribArray(), but you had to call them every time that you used a buffer. In OpenGL 3.x, you just set up the array once.

Going back to OpenGL 1.5, you could actually use buffers, but you used a separate function to bind each kind of data. For example, glVertexPointer() was for vertex data, and glNormalPointer() was for normal data. Prior to OpenGL 1.5, there weren't buffers, but you could use pointers into your application memory.

OpenGL 4.3 / ARB_vertex_attrib_binding

In 4.3, or if you have the ARB_vertex_attrib_binding extension, you can specify the attribute format and the attribute data separately. This is nice because it lets you easily switch one vertex array between different buffers.

GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib, 0);
glVertexAttribBinding(normal_attrib, 0);
glVertexAttribBinding(texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first! Nice!
glBindVertexBuffer(0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);

OpenGL 4.5 / ARB_direct_state_access

In OpenGL 4.5, or if you have the ARB_direct_state_access extension, you no longer need to call glBindBuffer() or glBindVertexArray() just to set things up... you specify the arrays and buffers directly. You only need to bind the array at the end to draw it.

GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.

// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib, 0);
glVertexArrayAttribBinding(array, normal_attrib, 0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);

// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);

ARB_direct_state_access is nice for a lot of reasons. You can forget about binding arrays and buffers (except when you draw) so you don't have to think about hidden global variables that OpenGL is tracking for you. You can forget about the difference between "generating a name for an object" and "creating an object" because glCreateBuffer() and glCreateArray() do both at the same time.

Vulkan

Vulkan goes even farther and has you write code like the pseudocode I wrote above. So you'll see something like:

// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0; // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]

Difference between glBindBuffer and glBindBufferBase

They're not used for the same purpose.

glBindBuffer is used to bind a buffer to a specific target so that all operations which modify that target are mapped to that buffer afterwards.

glBindBufferBase is used for a totally different purpose, it's used bind a buffer to a specific binding point in an indexed array (when data is not supposed to be directly modified but rather used). While this may seem convoluted it's really easy to see. Assume you want to pass an uniform block to your shader, then you have a table which maps named buffers to specific indices in an array which are then mapped to bindings in a shader like in the following figure:

Sample Image

so glBindBufferBase is creating the arrows on the right in which you specify the index, while glBindBuffer is just binding the buffer to the specific target.

You would then use glGetUniformBlockIndex to get the correct index in the shader which is then linked to the binding point (the left arrows) through glUniformBlockBinding.

how does a VBO get attached to a VAO

how does a VBO get attached to a VAO

It is attached when glVertexAttribPointer is called.

Each attribute which is stated in the Vertex Array Objects state vector may refer to a different Vertex Buffer Object. This reference is stored when glVertexAttribPointer is called.
Then the buffer which is currently bound to the target ARRAY_BUFFER is associated to the attribute and the name (value) of the object is stored in the state vector of the VAO. The ARRAY_BUFFER binding is a global state.

This behaves different to the Index Buffer (ELEMENT_ARRAY_BUFFER). The index buffer binding is stated within the Vertex Array Object. When a buffer is bound to the target ELEMENT_ARRAY_BUFFER, then this buffer is associated to the vertex array object which is currently bound.


That means when you call glBindBuffer(GL_ARRAY_BUFFER, VBO);, then a global state is set that stores VBO. glVertexAttribPointer retrieves VBO from the global state and assoicates it to VAO.
When glBindBuffer(GL_ARRAY_BUFFER, ...) is called again, then the previous binding is lost, but of course it does not change any state of the VAO.

glBindVertexArray(VAO);              # bind VAO (global state)

glBindBuffer(GL_ARRAY_BUFFER, VBO1); # bind VBO1 (global state)
glVertexAttribPointer(0, ...); # associate VBO1 to attribute 0 in VAO

glBindBuffer(GL_ARRAY_BUFFER, VBO2); # bind VBO2 (change global state, VBO1 binding is lost)
glVertexAttribPointer(1, ...); # associate VBO2 to attribute 1 in VAO

What does the GL_ARRAY_BUFFER target mean in glBindBuffer?

In General

Most OpenGL objects must be bound to locations in the OpenGL context called "targets" for them to be used. A target is nothing more than a place in the context where objects are bound.

Different object types (buffers, textures, etc) have different sets of targets. Generally speaking, each target has a specific meaning: to bind one object to one target means that you want to use that object in whatever manner that target uses objects bound to it.

Binding an object to one target does not affect whether the object is bound to another target (unless it's a texture object; they treat targets differently).

There are functions that modify objects or query data from bound objects. They take a target to which the object they are modifying/querying has been bound.

GL_ARRAY_BUFFER

The GL_ARRAY_BUFFER target for buffer objects represents the intent to use that buffer object for vertex attribute data. However, binding to this target alone doesn't do anything; it's only the call to glVertexAttribPointer (or equivalent functions) that uses whatever buffer was bound to that target for the attribute data for that attribute.

Understanding binding in OpenGL?

glGenVertexArrays() is akin to malloc() or new in that it allocates resources (here, the IDs for Vertex Array Objects or VAOs) that you can use later.

glBindVertexArray() controls which VAO is active -- part of the OpenGL global state. It takes a VAO ID that was allocated by glGenVertexArrays(), creates an underlying OpenGL object for it (if needed), and makes it the current one so that other OpenGL operations apply to it. (It can also de-activate the current VAO.)

More details can be found here.

Is glGenBuffers really important if we have glBindBuffer?

No, this code will work only in a compatibility profile context (or OpenGL ES).

See OpenGL 4.6 API Core Profile Specification - 2.6.1 Object Management- page 28

[...] the command GenBuffers returns one or more previously unused buffer object names.

Generated names are marked by the GL as used, for the purpose of name generation only. Object names marked in this fashion will not be returned by additional calls to generate names of the same type until the names are marked unused again by deleting them [...]

This means that glGenBuffers does nothing else than reserving names (values). Further calls to glGenBuffers will not return the same values.
If glGenBuffers is always used to get name values for buffer objects, then you can be sure that the value isn't already used for an other buffer object.

But in desktop OpenGL core profile specification it is not allowed to use an name for glBindBuffer, which wasn't reserved (returned) by glGenBuffers.

See OpenGL 4.6 API Core Profile Specification - 6.1 Creating and Binding Buffer Objects - page 62

An INVALID_OPERATION error is generated if buffer is not zero or a name
returned from a previous call to GenBuffers
, or if such a name has since been
deleted with DeleteBuffers.

This part of the specification is missing in the OpenGL 4.6 API Compatibility Profile Specification - 6.1 Creating and Binding Buffer Objects - page 62

That is a bit confusing, but that's the specification.

This behavior is verifiable with the code of your question. The following code returns no error using a compatibility profile context but returns GL_INVALID_OPERATION using a core profile context:

GLuint bar = 70;
glBindBuffer(GL_ARRAY_BUFFER, bar);
GLenum error = glGetError();

glBindBuffer and direct state access?

The purpose of direct state access is not to remove object binding from your application completely (that would be the purpose of the various "bindless" extensions). The purpose of direct state access is to allow you to access the state of an object without having to bind it (ie: directly).

Pre-DSA, you had to bind a buffer just to allocate storage, upload data, etc. With DSA functions, you don't have to. You pass the buffer directly to the function you use to manipulate its state.

But to actually use buffers in a rendering process, you must still bind it to the context or attach it to some other object which itself will get bound to the context.

To use a buffer's storage as uniform data, it must be bound to the context with glBindBufferRange(GL_UNIFORM_BUFFER (or equivalent calls). To use a buffer to store vertex data, you must attach it to a VAO through glVertexArrayVertexBuffer, then bind that VAO to the context. To use a buffer for pixel transfer operations, you must bind the buffer to the appropriate location with glBindBuffer. And so forth.

The same goes for using a buffer as the source for indirect command execution. That's a rendering operation, so the buffer must be bound to the indirect target before issuing the indirect command.

But it only needs to be bound at the time you issue that command. You don't have to bind it immediately after creation. Just before you actually call glDrawArraysIndirect.



Related Topics



Leave a reply



Submit