1
\$\begingroup\$

I am very new to open gl and am struggling to understand relation between vaos vbos and ibos like how is currently bound vao related to currently bound ibo?

now i am trying to render 4 triangles with vertex data and index data for ibo as

float vertices[] = {
        -0.5f, -0.5f,  2.0f,     1.0f, 0.0f, 0.0f ,1.0,
         0.5f, -0.5f,  2.0f,     0.0f, 1.0f, 0.0f ,1.0,
         0.0f,  0.5f,  2.0f,     0.0f, 0.0f, 1.0f ,1.0,

         0.0f,  0.0f,  1.0f,     1.0f, 1.0f, 0.0f ,1.0,
         0.0f,  0.0f,  3.0f,     0.0f, 1.0f, 1.0f ,1.0,
         0.0f, -1.0f,  2.0f,     1.0f, 0.0f, 1.0f ,1.0,
    };

    float vertices1[] = {
        -0.5f, -0.5f,  0.0f,     1.0f, 0.0f, 0.0f ,1.0,
         0.5f, -0.5f,  0.0f,     0.0f, 1.0f, 0.0f ,1.0,
         0.0f,  0.5f,  0.0f,     0.0f, 0.0f, 1.0f ,1.0,

         0.0f,  0.0f, -1.0f,     1.0f, 1.0f, 0.0f ,1.0,
         0.0f,  0.0f,  1.0f,     0.0f, 1.0f, 1.0f ,1.0,
         0.0f, -1.0f,  0.0f,     1.0f, 0.0f, 1.0f ,1.0
    };

    unsigned indices[] = {
        0,1,2,3,4,5
    };

    unsigned indices1[] = {
         0,1,
         1,2,
         2,0,
         3,4,
         4,5,
         5,3
    };

and i am creating vaos and ibos as:

unsigned int VBO[2], VAO[2], IBO[2];
    glGenVertexArrays(2, VAO);
    glGenBuffers(2, VBO);
    glGenBuffers(2, IBO);
    glBindVertexArray(VAO[0]);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);



    glBindVertexArray(VAO[1]);

    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices1), vertices1, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);
    //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

And I am drawing it as:

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO[0]);
        glBindVertexArray(VAO[0]);

        glDrawElements(GL_TRIANGLES, sizeof(indices1) / sizeof(unsigned), GL_UNSIGNED_INT, 0);

        glBindVertexArray(0));
        //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);


        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO[1]);
        glBindVertexArray(VAO[1]);

        glDrawElements(GL_LINES, sizeof(indices1)/sizeof(unsigned), GL_UNSIGNED_INT, 0);

        glBindVertexArray(0));
        //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

If i dont unbind vao in the draw call then it will not be drawn as expected i.e. partially drawn lines or missing triangles. Are'nt vaos supposed to override any previous bindings and bind a new one specified? Even if i dont unbind vao after setting glVertexAttribPointer I get a blank screen! Then I got Totally confused on when to bind before doing what. What does vao hold other than vertex attribute?like can i unbind ibo before vao?

\$\endgroup\$
1
  • \$\begingroup\$ If you are not restricted to an older OpenGL version i highly recommend you to use the new OpenGL 4.5 Direct State Access. At least for me the whole VBO/VAO thing felt a lot more intuitive and easier to understand using those functions instead. \$\endgroup\$
    – Eric
    May 30, 2020 at 15:44

2 Answers 2

1
\$\begingroup\$

Unbinding is an OpenGL anti-pattern; it doesn't exist in OpenGL as designed, it's not necessary except in very limited cases, it often doesn't do what you think it does, but yet you see it used in tutorials and people have assimilated the thinking that you need to do it.

This is an unfortunate side-effect of old (non-DSA) OpenGL using the same state for drawing as it used for creating and modifying objects. As a result of that, code to accomplish the one will dirty the state used for accomplishing the other, and unbinding was used as a crutch to help prevent that (or at worst, assist detecting errors arising from it).

However, for many object types, object 0 is actually a valid object name and has special meaning.

  • For vertex buffer objects, binding object 0 will switch the pipeline back to using client-side (i.e. system memory) arrays.

  • For vertex array objects, binding object 0 will switch the pipeline back to using global array state.

  • For texture objects, binding object 0 will switch the pipeline back to OpenGL 1.0 behaviour before texture objects existed.

  • For sampler objects, binding object 0 will switch the pipeline back to using per-texture parameters.

  • Etc.

The answer to the question of "when to unbind" is therefore: never. The exception is when the GL specification might require an object to not be bound, e.g. before deleting it; otherwise you should never unbind.

Instead the correct pattern to use is to just bind everything you need immediately before using it. Don't rely on global state, and definitely don't assume that state might carry over from previous operations. Better again, just use the modern API with DSA.

\$\endgroup\$
0
\$\begingroup\$

I don't do it that way any more. In cases when the shader-pipeline doesn't change and the interface (vertex attributes) from the application stays the same one can create a vao and just switch the buffers before the draw calls. Example with plain c-code, to sketch the way, OpenGL 4.5, a vertex array with a static and a dynamic buffer and one vertex attribute, for brevity:

// Configure vertex array and buffers
glCreateVertexArrays( 1, &(i->vertex_array) );
// Only one vec4f attrib: .xy = coords, .zw = texture coords from font atlas
glVertexArrayAttribFormat( i->vertex_array, 0, 4, GL_FLOAT, GL_FALSE, 0 );
glEnableVertexArrayAttrib( i->vertex_array, 0 );
glCreateBuffers( 1, &(i->static_vertex_buffer) );
glCreateBuffers( 1, &(i->dynamic_vertex_buffer) );

Copy in static data to the static buffer (1 time only):

glNamedBufferData( in->static_vertex_buffer, (GLsizeiptr)buffer_size, buf, GL_STATIC_DRAW );

Prepare dynamic buffer storage. Size cannot be changed later, contents can:

glNamedBufferStorage( in->dynamic_vertex_buffer, size, NULL, GL_MAP_WRITE_BIT );

Every frame, update dynamic buffer:

// obtain a pointer
vec4f* buf = glMapNamedBuffer( in->dynamic_vertex_buffer, GL_WRITE_ONLY );
// ... use the pointer to write to the buffer, e.g. via memcpy() ...
if( GL_TRUE != glUnmapNamedBuffer( in->dynamic_vertex_buffer ) )
    // do something about it

And in the draw you just bind the array. You can switch the buffers as needed:

// draw static and dynamic buffer
glBindVertexArray( i->vertex_array );
glVertexArrayVertexBuffer( i->vertex_array, 0, i->static_vertex_buffer, 0, sizeof( vec4f ) );
glDrawArrays( GL_TRIANGLES, 0, i->num_static_vertices );
glVertexArrayVertexBuffer( i->vertex_array, 0, i->dynamic_vertex_buffer, 0, sizeof( vec4f ) );
glDrawArrays( GL_TRIANGLES, 0, i->num_dynamic_vertices );

If you draw elements you glCreateBuffer it once and connect it to the vao via glVertexArrayElementBuffer. If its contents change map/write/unmap as above. During drawing, just use the shader and the vao and switch buffers as needed, or keep them when they don't change.

Don't forget error checking the return value of glUnmapBuffer.

Hope that clarifies.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .