glGenVertexArrays

#define GLEW_STATIC
#include <GL/glew.h>
#include <GL/glfw3.h>

GLuint VBO[2], VAO[2];

void init()
{
    static const GLfloat vertices[] =
    {
        // First triangle
        -0.9f, -0.5f, 0.0f,  // Left
        -0.0f, -0.5f, 0.0f,  // Right
        -0.45f, 0.5f, 0.0f,  // Top
    };

    static const GLfloat vertices2[] =
    {
        // Second triangle
        0.0f, -0.5f, 0.0f,  // Left
        0.9f, -0.5f, 0.0f,  // Right
        0.45f, 0.5f, 0.0f   // Top
    };

    glGenVertexArrays(2, VAO);

    glBindVertexArray(VAO[0]);
    glGenBuffers(2, VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    glBindVertexArray(VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

static void render()
{
    glClearColor(0, 0, 0.2, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    glBindVertexArray(VAO[0]);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    glBindVertexArray(VAO[1]);
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

int main()
{
    glfwInit();
    GLFWwindow *window = glfwCreateWindow(640, 480, "VAO", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glewInit();
    init();

    while (!glfwWindowShouldClose(window))
    {
        render();

        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

When I run this program, it works fine: [ATTACH=CONFIG]1904[/ATTACH]
But when I use

VAO[0] = 1; VAO[1] = 2;

instead of

glGenVertexArrays(2, VAO);

I get the following result: [ATTACH=CONFIG]1905[/ATTACH]
How does glGenVertexArrays actually work?

I am confused, that you can actually see anything if you do what you described… Would be interesting to know if glGetError() will at least give you an error for that :confused:

However, I am not an expert on the details but I think I can help you a little bit. OpenGL is an interface that helps you to communicate with your GPU (or its driver). It also manages the objects that live in your GPUs memory and objects like the VAO that help you in other kind of ways. Lets have a look at an easier to understand type than the VAO, a vertex buffer object: glGenBuffers(2, VBO) has an identical interface and does pretty much the same if you look on it from the outside: Writing some different numbers into an array. But internally it creates some kind of list for objects that will live in the GPUs memory and that OpenGL should manage for you. Think of a simple c++ class like this (might contain typos and other mistakes since i did not compile it):


class BufferObjectManager
{
std::vector<BufferObject*> mData;
int mBoundDataIndex = 0;

public:

void GenBuffer(int num, int* array)
{
    for(int i = 0; i<num; ++i)
    {
        mData.push_back(nullptr);
        array[i] = mData.size();
    }
}

void BindBuffer(int index)
{
    mBoundDataIndex = index;
}

void BufferData(args...)
{
    if(mBoundDataIndex >0 )
    {
        int vectorIndex = mBoundDataIndex - 1;
        if(mData[vectorIndex] != nullptr)
            delete mData[vectorIndex];
        mData[vectorIndex] = new BufferObject(args...);
    }
}

~BufferObjectManager()
{
    // free all managed objects
    for(int i = 0; i<mData.size(); ++i)
    {
        if(mData[i] != nullptr)
            delete mData[i];
    }
}
};


This class has an internal vector where it stores all its managed “BufferObjects”. The GenBuffer function will add an arbitrary number of new but empty (nullptr) entries to the internal vector and writes you their direct access indices increased by 1 to an array you passed to the function. It increases the index by 1 since arrays in c++ start with zero and we want zero to have a special “do nothing” or “is unbound” functionality. BindBuffer sets an internal variable that determines which of the vectors objects will be affected by all modification commands. BufferData will create a new object in memory and stores it in the vector element that corresponds to the last index you passed with BindBuffer. For Vertex Array Objects it should be a quiet similar management mechanism but adjusted to the needs of this object type. I kept the names similar to the OpenGL names and I hope that this little code example will now let you understand why writing


VAO[0] = 1; VAO[1] = 2;

can’t replace


glGenVertexArrays(2, VAO);

The first variant just writes some meaningless numbers. The second will create space for (and in contrast to a buffer object also create) a vertex array object and return their indices/handles to you. If you call glBindVertexArray with some made up numbers it is basically the same as reading the value of the 10th element of an array with a size of 2. The program might run without crashing but the result is undefined. And that is what you get in your case. It runs… but it does not do what you want.

So try to call glGetError —> glGetError - OpenGL 4 Reference Pages and compare both cases.

As I said, I am no expert, so I don’t know the exact implementation details. Mayby they did something totally different and all the things I wrote are absolute BS, but at least it should give you a hint why things behave the way they do.

It’s entirely up to the implementation. All that’s relevant here is that the specification of glBindVertexArray() states that:

GL_INVALID_OPERATION is generated if array is not zero or the name of a vertex array object previously returned from a call to glGenVertexArrays.

In other words, you can’t (legitimately) allocate VAO names yourself; you have to use glGenVertexArrays().

As for why your program partially works, it’s possible that something (maybe GLEW or GLFW, or maybe the driver itself) uses VAO 1 for its own purposes, so using that name in your own code conflicts with that.