1
\$\begingroup\$

I'm rendering various elements in OpenGL ES 2 on Android, and I'd like to pack the vertex attributes of the whole scene into a single vertex buffer object (VBO).

My question is, if these objects have different sets of attributes, strides and offsets, can I still pack this data into a single VBO and draw everything using it?

Most of these elements have 2 floats for position and 2 floats for texture coords (X, Y, S, T) as vertex attributes, and they do get drawn correctly using this:

@Override
public void onDrawFrame(GL10 glUnused) {
    glClear(GL_COLOR_BUFFER_BIT);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getBufferId());
    drawObject1();
    drawObject2();
    drawObject3();
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

public void drawObject1() {
    glUseProgram(object1Program);
    object1Program.setUniforms(finalMatrixObject1, object1Texture);

    glVertexAttribPointer(object1Program.getPositionAttributeLocation(), 2, GL_FLOAT, false, 16, 0);
    glEnableVertexAttribArray(object1Program.getPositionAttributeLocation());
    glVertexAttribPointer(object1Program.getTextureCoordinatesAttributeLocation(), 2, GL_FLOAT, false, 16, 8);
    glEnableVertexAttribArray(object1Program.getTextureCoordinatesAttributeLocation());

    glDrawArrays(GL_TRIANGLES, 0, 6);
}

public void drawObject2() {
    glUseProgram(object2Program);
    object2Program.setUniforms(finalMatrixObject2, object2Texture);

    glVertexAttribPointer(object2Program.getPositionAttributeLocation(), 2, GL_FLOAT, false, 16, 0);
    glEnableVertexAttribArray(object2Program.getPositionAttributeLocation());
    glVertexAttribPointer(object2Program.getTextureCoordinatesAttributeLocation(), 2, GL_FLOAT, false, 16, 8);
    glEnableVertexAttribArray(object2Program.getTextureCoordinatesAttributeLocation());

    glDrawArrays(GL_TRIANGLES, 6, 6);
}

But I have weird artifacts and coords showing (if at all) when I try to render an element with a different pattern of vertex attributes, for example just 2 floats for position (X Y), or 3 floats for position and 2 floats for texture coord (X Y Z S T).

public void drawObject3() {
    glUseProgram(object3Program);
    object3Program.setUniforms(finalMatrixObject3);

    glVertexAttribPointer(object3Program.getPositionAttributeLocation(), 2, GL_FLOAT, false, 8, 0);
    glEnableVertexAttribArray(object3Program.getPositionAttributeLocation());

    glDrawArrays(GL_TRIANGLES, 12, 6);
}

To resume the situation, I have a VBO packed this way (it differs a bit from my previous example):

6 * (X, Y, S, T)
6 * (X, Y, S, T)
6 * (X, Y, S, T)
6 * (X, Y)
36 * (X, Y, Z, S, T)

Is it possible to do what I'm trying to do? Or do I have to use different VBOs for every element with a unique set of vertex attributes?

If it's possible, what am I doing wrong? Before moving to VBO everything was working fine.

\$\endgroup\$
  • 1
    \$\begingroup\$ I think it is calculating the starting offset wrong. Try setting the last parameter of glVertexAttribPointer to 12 * 16 and the second of glDrawArrays to 0 instead of 12 in drawObject3. If that doesn't work you could also try 24 instead of 12 (but leave attribpointer at 0). \$\endgroup\$ – Alex - GlassEditor.com Oct 1 '19 at 8:35
  • \$\begingroup\$ @Alex It's working, yeeees! If you can turn this into an answer (an explain it a bit more) I'll be happy to grant you the bounty. I need more explanation because object3 gets drawn now - with offset 12*16 (actually 18*16 in my example) and drawarrays 0 as first -, but when I try to follow the same logic, my object4 still gets artifacts, so I'm not getting it. BTW, thank you! :) \$\endgroup\$ – Rodia Oct 1 '19 at 9:04
  • \$\begingroup\$ @Alex update: All objects working now and get drawn correctly. I still don't get why ´dataOffset´ and the "first vertex" param in ´glDrawArrays´ are working for the batch of first objects with the same set of attributes. \$\endgroup\$ – Rodia Oct 1 '19 at 9:18
3
+50
\$\begingroup\$

You were calculating the starting offsets into the buffer to render from incorrectly. The byte offset used is pointer + first * stride. When you call glVertexAttribPointer in drawObject3 you set the stride to 8 and pointer to 0, so the offset is 8 * 12 which puts you at the start of your second row of data, not the row you expected. The easy way to do it correctly is to set pointer to where the data you want to render starts (18 * 16) and first to 0.

For the last row it would be 18 * 16 + 6 * 8.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.