17

I am having a problem getting my vertex array pointed to properly:

const float vertices[] = {
/* position */ 0.75f, 0.75f, 0.0f, 1.0f, /* color */ 1.0f, 0.0f, 0.0f, 1.0f,
/* position */ 0.75f, -0.75f, 0.0f, 1.0f, /* color */ 0.0f, 1.0f, 0.0f, 1.0f,
/* position */ -0.75f, -0.75f, 0.0f, 1.0f, /* color */ 0.0f, 0.0f, 1.0f, 1.0f, };

...

glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObject);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void*)16);

glDrawArrays(GL_TRIANGLES, 0, 3);

glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);

I do not understand how stride and offset work. What is the correct way of going about using glVertexAttribPointer() in my situation?

Cartier
  • 429
  • 4
  • 15
user2350858
  • 681
  • 2
  • 10
  • 16

2 Answers2

67

Since glVertexAttribPointer often make troubles, I try to further explain it here.

The formula to calculate the start pos of the i-th attribute in attributes array is :

startPos(i) = offset + i * stride (From derhass' another answer)

And explained in the graph below : tightly packed arrtibute array


If you need code example, continue reading.

From Formatting VBO Data, we know we can manage our vertex data in three format. Make a example of drawing a triangle , with vert color and texture color mixed, here is the way to prepare vert attribute data:


#way1 Each attribute a VBO.

this format like : (xyzxyz...)(rgbrgb...)(stst....), and we can let both sride = 0 and offset = 0.

void prepareVertData_moreVBO(GLuint& VAOId, std::vector<GLuint>& VBOIdVec)
{
    // positon
    GLfloat vertPos[] = {
        -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f,
    };

    // color
    GLfloat vertColor[] = {
      1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
    };

    // texture coordinate
    GLfloat vertTextCoord[] = {
      0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f
    };
    
    GLuint VBOId[3];
    
    glGenVertexArrays(1, &VAOId);
    glBindVertexArray(VAOId);
    glGenBuffers(3, VBOId);

    // specify position attribute
    glBindBuffer(GL_ARRAY_BUFFER, VBOId[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertPos), vertPos, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(0);

    // specify color attribute
    glBindBuffer(GL_ARRAY_BUFFER, VBOId[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertColor), vertColor, GL_STATIC_DRAW);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(1);

    // specify texture coordinate attribute
    glBindBuffer(GL_ARRAY_BUFFER, VBOId[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertTextCoord), vertTextCoord, GL_STATIC_DRAW);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    VBOIdVec.push_back(VBOId[0]);
    VBOIdVec.push_back(VBOId[1]);
    VBOIdVec.push_back(VBOId[2]);
}

#way2: Each attribute is sequential, batched in a single VBO.

this format is like: (xyzxyzxyz... rgbrgb... ststst...), we can let stride=0 but offset should be specified.

void prepareVertData_seqBatchVBO(GLuint& VAOId, std::vector<GLuint>& VBOIdVec)
{
    GLfloat vertices[] = {
      -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f,  // position
      1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,   // color
      0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f     // texture coordinate
    };
    
    GLuint VBOId;
    glGenVertexArrays(1, &VAOId);
    glBindVertexArray(VAOId);
    
    glGenBuffers(1, &VBOId);
    glBindBuffer(GL_ARRAY_BUFFER, VBOId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // specifiy position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0); // stride can aslo be 3 * sizeof(GLfloat)
    glEnableVertexAttribArray(0);

    // specify color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(9 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    // specify texture coordinate
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)(18 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    VBOIdVec.push_back(VBOId);
}

#way3: Interleaved attribute in a single VBO

this format is like: (xyzrgbstxyzrgbst...),we have to manually specify the offset and stride .

void prepareVertData_interleavedBatchVBO(GLuint& VAOId, std::vector<GLuint>& VBOIdVec)
{
    // interleaved data
    GLfloat vertices[] = {
        -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,  // 0
        0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // 1
        0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,  // 2
    };
    
    GLuint VBOId;
    glGenVertexArrays(1, &VAOId);
    glBindVertexArray(VAOId);
    glGenBuffers(1, &VBOId);
    glBindBuffer(GL_ARRAY_BUFFER, VBOId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // specify position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    // specify color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
        8 * sizeof(GLfloat),(GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    // specify texture coordinate
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 
        8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    VBOIdVec.push_back(VBOId);
}

And thanks for derhass's answer.

Elias Benevedes
  • 363
  • 1
  • 8
  • 26
wangdq
  • 1,874
  • 17
  • 26
  • Wow, I was trying to wrap my brain around the differences of the 3 methods for the better part of a day (mostly interleaving vs the others, and how to use the offset properly), then I saw this, side by side, and it all became clear. Thanks. ;) Any idea which is usually faster? – James Wilkins Jan 04 '17 at 02:00
  • @JamesWilkins According to recommendations I've read (for example Apples IOS recommendation, the version with interleaved data is faster. – Viktor Sehr Jun 20 '18 at 13:14
  • I guess that makes sense, since the system can easily "blit" the values into a struct instead of copying each one by one from separate arrays. – James Wilkins Jun 21 '18 at 08:48
  • 1
    `sizeof(GL_FLOAT)` looks incorrect and works by accident. Should be `sizeof(GLfloat)` – Hertz Nov 28 '18 at 10:07
  • @ViktorSehrvim the fastest method is instead the way two. It is the fastest way because you can use the full cache to upload the vertexes. – Elvis Dukaj Feb 06 '23 at 12:45
23

Stride and offset are specified in bytes. You are using an interleaved vertex array with position and color both as 4 floats. To get from th i-th element in a particular attribute array to the next one, there is the distance of 8 floats, so stride should be 8*sizeof(GLfloat). The offset is the byte position of the first element of each attribute array in the buffer, so in your example for position it is 0, and for color, it is 4*sizeof(GLfloat)

Ryan T. Grimm
  • 1,307
  • 1
  • 9
  • 17
derhass
  • 43,833
  • 2
  • 57
  • 78
  • what should my two glVertexAttribPointer stride and offset values be? – user2350858 May 06 '13 at 01:42
  • 1
    @user2350858: As I already wrote in the answer: stride is 8*sizeof(GLfloat) (so typically 32) and offset 0 for position and 4*sizeof(GLfloat) for color (as you already do) – derhass May 06 '13 at 19:46
  • The type of offset must be `const void*`, how would you achieve this? I considered `nullptr + offset`, but the type is wrong. I tried `(void*)0 + offset` but that isn't working for me yet (not sure what else could be wrong.) – John P Oct 06 '18 at 21:19