11

Here is the deal: If I leave the code with glGenBuffers(1, vertexBuffers), the code compiles and works. But, I thought that it should be 2 since vertexBuffers is of size two.

Am I missing something?

Code below:

-(void)drawRect:(NSRect)dirtyRect
{    
    // get program ID for shader program
    GLuint programID = [self loadShaders];

    // get new dimensions
    NSSize dim = [self frame].size;

    // clear the background with color
    glClearColor(0.0, 0.0, 0.0, 0.4);
    glDepthRange(0.1, 100.0);
    glViewport(0, 0, dim.width, dim.height);
    glClear(GL_COLOR_BUFFER_BIT);

    // vertex data
    GLfloat vertexPositionData[] = {-dim.width/2, -dim.height/2, 0.0, 5.0,
        dim.width/2, -dim.height/2, 0.0, 5.0,
        0.0, dim.height/2, 0.0, 5.0};

    GLfloat vertexColorData[] = {1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 1.0, 0.5};

    GLfloat scaleMatrixData[] = {1/(dim.width/2), 0.0, 0.0, 0.0,
        0.0, 1/(dim.height/2), 0.0, 0.0,
        0.0, 0.0, 1.0, 0.0,
        0.0, 0.0, 0.0, 1.0};

    GLint scaleMatrixUniform = glGetUniformLocation(programID, "scaleMatrix");

    // generate a buffer for our triangle
    glGenBuffers(1, vertexBuffers);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositionData), vertexPositionData, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexColorData), vertexColorData, GL_STATIC_DRAW);
    //glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(programID);

    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[0]);
    glEnableVertexAttribArray(VERTEX_POS_INDEX);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffers[1]);
    glEnableVertexAttribArray(VERTEX_COLOR_INDEX);
    glVertexAttribPointer(VERTEX_POS_INDEX, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, vertexPositionData);
    glVertexAttribPointer(VERTEX_COLOR_INDEX, VERTEX_COLOR_SIZE, GL_FLOAT, GL_FALSE, 0, vertexColorData);

    glUniformMatrix4fv(scaleMatrixUniform, 1, GL_FALSE, scaleMatrixData);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glUseProgram(0);

    // flush buffer
    glFlush();
    [[self openGLContext] flushBuffer];
}
0xCursor
  • 2,242
  • 4
  • 15
  • 33
xBACP
  • 531
  • 1
  • 3
  • 17

3 Answers3

31

glGenBuffers doesn't work quite like what you expect. When you call glGenBuffers, it doesn't actually create anything. It just returns a list of integers that are not currently used as buffer names.

The actual 'object' is not created until you call glBindBuffer. So you can just make up any integer you like and pass it to glBindBuffer and a valid buffer will be created at that index. glGenBuffers is actually not required at all, it's just there as a convenience function to give you an unused integer.

So if you just create an array of random integers, as long as none of them overlap, you can use that as your list of buffers without calling glGenBuffers. That's why your code works whether you tell glGenBuffers to create 1 or 2 buffers. As long as you have two buffers with two different names, it doesn't matter where the integer came from.

A little demonstration:

int buf;
glGenBuffers(1, &buf);
glIsBuffer(buf); //FALSE - buffer has not been created yet
glBindBuffer(GL_ARRAY_BUFFER, buf);
glIsBuffer(buf); //TRUE - buffer created on bind
Tim
  • 35,413
  • 11
  • 95
  • 121
  • 1
    In more recent OpenGL `glGenBuffers` is required afaik. – KillianDS Aug 24 '12 at 07:18
  • 2
    Also, IsBuffer should return true there according to the spec: `returns TRUE if buffer is the name of an buffer object. If buffer is zero, or if buffer is a non-zero value that is not the name of an buffer object, IsBuffer returns FALSE.` – KillianDS Aug 24 '12 at 07:22
  • 1
    Spec about `glGenBuffers` requirement: `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` – KillianDS Aug 24 '12 at 07:24
  • Hmm, I wasn't aware that had changed. Looking at the manual page for glIsBuffer, I see `A name returned by glGenBuffers, but not yet associated with a buffer object by calling glBindBuffer, is not the name of a buffer object. `, which seems to disagree with the other statements. – Tim Aug 24 '12 at 07:39
  • 1
    @KillianDS Looks like this might have changed in version 3.2, for the core profile. Thanks for bringing it to my attention. Changelog: `fix BindBuffer (section 2.9.1), BeginQuery (section 2.15), and BindTexture (section 3.8.1) to only generate errors for user generated names in the core profile` – Tim Aug 24 '12 at 07:52
  • AFAIK, GenBuffers looks up X amount of buffer ID's that are currently not in use and returns them to you in the form of an integer ID (or an array of integers if you generated more than one). – Stradigos Sep 25 '13 at 18:52
  • As OP says, you then solidify that contract by binding those ID's to those actual buffers. In other words, you associate the ID's with the actual buffers by binding them. KEEP IN MIND that those ID's are not released for subsequent GenBuffer calls unless you explicitly delete them using DeleteBuffers. – Stradigos Sep 25 '13 at 19:00
  • I am also learning **OpenGL**. So, ``glGenBuffers`` just assigns 1 to ``buf``. Then using this ID number a pointer of type ``GL_ARRAY_BUFFER`` is created. Then when I call ``glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);`` it creates memory in heap and assigns those vertices to the heap memory. Is my understanding correct? Assuming ``vertices[] = { // vertices of triangle }`` Is my understanding correct? –  Aug 17 '22 at 04:43
11

Assuming that vertexBuffers is declared as something like:

GLuint vertexBuffers[2];

then your code is probably not quite doing what you think. The first argument to glGenBuffers() is the number of buffers you want to generate, and the second argument is a pointer to an array of that many buffers. So you probably want to do:

glGenBuffers (2, vertexBuffers);

This tells OpenGL to generate 2 buffer names and to store them in 2 contiguous locations in memory starting at the address of vertexBuffers.

user1118321
  • 25,567
  • 4
  • 55
  • 86
  • I understand your comment. However, vertexBuffers is of size 2, but in my code I have this: glGenBuffers(1, vertexBuffers) and the program works. If I change the 1 to 2, it no longer displays the triangle. – xBACP Aug 24 '12 at 16:57
  • If you look at Tim's response below, he explains why it works even when you don't generate both buffers. It's a quirk of how OpenGL works, but is probably not what you intended. – user1118321 Aug 24 '12 at 21:20
2

I figured this out yesterday, I need to do a glBindBuffer(GL_ARRAY_BUFFER, 0) after each the glBufferData(...).

xBACP
  • 531
  • 1
  • 3
  • 17