19

I want to know the simplest method for using vbo's in OpenGL... I have tried running a few examples that work but are clouded with all other information thats making it really confusing me... at the moment this is what i have

GLuint vboId = 0;
const int trisize = (m_tris.size()/2)*3;//m_tris is an index array for verts and normals
GLfloat* vertices = new GLfloat[trisize];
GLfloat* normals = new GLfloat[trisize];

int j=0;
for (int i=0; i<m_tris.size(); i+=2) {

    normals[j] = m_normals[m_tris[i+1]*3];
    vertices[j++] = m_vertices[m_tris[i]*3];
    normals[j] = m_normals[m_tris[i+1]*3+1];
    vertices[j++] = m_vertices[m_tris[i]*3+1];
    normals[j] = m_normals[m_tris[i+1]*3+2];
    vertices[j++] = m_vertices[m_tris[i]*3+2];
} //im pretty sure this loop is right as its what i used before to display mesh correctly without vbo's using glVertex3f

glGenBuffersARB(1, &vboId);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertices)+sizeof(normals), 0, GL_STATIC_DRAW_ARB);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sizeof(vertices), vertices);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sizeof(vertices), sizeof(normals), normals);

glVertexPointer(sizeof(vertices), GL_FLOAT, 3, 0);
glNormalPointer(GL_FLOAT, 3, (void*)sizeof(vertices));

in a render method i have

glDrawArrays(GL_TRIANGLES, 0, this->getTriNum()); //0 is the vboId?

also i have a method that runs once...

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

when i try to run my code i get "EXC_BAD_ACCESS"

any advice on what im doing wrong... or a very simple implementation of a vbo would be very helpful

CurtisJC
  • 680
  • 3
  • 9
  • 23

4 Answers4

24

What you are doing looks right, except sizeof(vertices) gives you the size of the address, not the size of the array. So, when you call glBufferData, you are allocating 8 bytes and when you call glBufferSubData, you fill those 8 bytes with the first four bytes of the vertices array and then the first four bytes of the normals array. Then, when you go to call glDrawArrays, the first indice causes an array index out of bounds exception (Hence the EXC_BAD_ACCESS).

// Data
GLuint geometry_array = 0;
GLuint indice_array = 0;

GLfloat *geometry;
GLuint *indices;

You can do this initialization differently, but I interleave all my data into a 32 bytes per vertice array. Any way you choose to set up your array works, as long as you use glVertexPointer, glNormalPointer, and glTexCoordPointer correctly.

// Initialize

geometry = new GLfloat[8*num_geometry];
indices = new GLuint[num_indices];

/* Fill geometry: 0, 1, 2 = vertex_xyz 
 *                3, 4, 5 = normal_xyz
 *                6, 7 = tex_coord_uv
 */

glGenBuffers(1, &geometry_array);
glBindBuffer(GL_ARRAY_BUFFER, geometry_array);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat)*8*num_geometry, NULL, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat)*8*num_geometry, geometry);

glGenBuffers(1, &indice_array);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indice_array);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)*num_indices, NULL, GL_STATIC_DRAW);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(GLuint)*num_indices, indices);

The render function is five steps. First, bind the buffers so OpenGL knows where to find your geometry and indice data. Second, Enable the various client states for each type of data. If you leave one out, that type of data will not be rendered, and if leave all of them out, your entire object will not be rendered. Third, you have to tell OpenGL where each type of data is contained in your array. In this case the 32 byte geometry data starts with 12 bytes of vertex data, so the starting offset should be zero or NULL. Since each vertex-normal-texcoord combo is 32 bytes, that is the step size. "sizeof(GLfloat)8" says 32 bytes from offset NULL, there will be another vertice xyz group. In the case of the normals, each normal xyz group is 12 bytes after the offset for the vertice xyz group, hence "(float)(sizeof(GLfloat)*3)" as the starting offset. Again, the step size is 32 bytes. Fourth, you have to tell it to draw all of the triangles associated with the indice array. Finally, you should disable the client states, in case you want to use a different method of rendering.

//Render
// Step 1
glBindBuffer(GL_ARRAY_BUFFER, geometry_array);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indice_array);

// Step 2
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

// Step 3
glTexCoordPointer(3, GL_FLOAT, sizeof(GLfloat)*8, (float*)(sizeof(GLfloat)*5));
glNormalPointer(GL_FLOAT, sizeof(GLfloat)*8, (float*)(sizeof(GLfloat)*3));
glVertexPointer(3, GL_FLOAT, sizeof(GLfloat)*8, NULL);

// Step 4
glDrawElements(GL_TRIANGLES, num_indices, GL_UNSIGNED_INT, NULL);

// Step 5
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
Ned Bingham
  • 2,749
  • 18
  • 23
  • just to check, your geometry array contains vertex normal and texture coord data? so in my case i would do the same minus anything involving textures... glDrawElements is used when you have more than just vertex data, num_indices would be 2 for for verts and norms or 1(0 verts 1 norms)? sorry for the hassle, still pretty new to opengl and only been doing c++ since beginning of Jan – CurtisJC Feb 23 '11 at 14:51
  • 1
    one vertice can be a part of multiple faces. So, you have an unordered array of vertices, then you have a list of "indices." Indices are just like the index of an array, only each index in the indices array points to one 32 byte geometry in the geometry array. Four separate indices describe a quad, and three describe a triangle. So num_indices is equal to the number of faces you have times the number of vertices per face. so, for 5 quads, that would be 5*4 is 20. If you want to take out the tex coord data, each geometry would be 24 bytes long and the geometry array would be 6*num_geometry long – Ned Bingham Feb 23 '11 at 15:44
  • Then, you take out "glEnableClientState(GL_TEXTURE_COORD_ARRAY)," "glTexCoordPointer," and "glDisableClientState(GL_TEXTURE_COORD_ARRAY)." The indices array would be unchanged by removing texture coordinates. Only the geometry array would change. Also, dont forget to change any "sizeof(GLfloat)*8" to "sizeof(GLfloat)*6" – Ned Bingham Feb 23 '11 at 15:47
  • OK, code is pretty buggy as the mesh isnt being displayed as it should be... but at least its running now so il try to fix the bugs... any chance it would something to do with there per-vertex normals? as opposed to per triangle? – CurtisJC Feb 23 '11 at 15:59
  • Well, if it is displaying, but not displaying as it should, that means you set up your arrays wrong. In this case, you would loop through the data increasing the index i, then to access the vertex data from the geometry array you would use geometry[i*6 + 0] for x, geometry[i*6 + 1] for y, and geometry[i*6 + 2] for z. To access the normals, you would use geometry[i*6 + 3] for x, geometry[i*6 + 4] for y, and geometry[i*6 + 5] for z. – Ned Bingham Feb 23 '11 at 16:06
  • at the moment i have in geometry: all vertices then all normals... and in indices i have: vertex normal vertex normal etc... is this right? i think i might be going wrong with the indices – CurtisJC Feb 23 '11 at 16:11
  • you are going wrong with indices, the normals are indexed automatically when you index the vertex. When you index a vertex, the normal placed directly after that vertex in the geometry array will be indexed with the vertex. so you dont need to index the normals. The vertex normals are defined as the average of the normals of the faces that that vertex is a part of. – Ned Bingham Feb 23 '11 at 16:17
  • ok... well now i know how its indexed i should be able to fix it... its just im reading in from a collada doc which stores the index as a vertex then its normal... thanks for you help :D – CurtisJC Feb 23 '11 at 16:31
3

This is a VBO tutorial with an working example. The example seams to be simple, so you might use it as a reference.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
1

OpenGL - VBO, Shader, VAO has a simple example which shows the basic sequence of API calls required for VBO based rendering. The example transitions from legacy immediate mode to modern shader based rendering.

The example code can be accessed on Github project OpenGl-Render and runs on both Windows and Linux.

ap-osd
  • 2,624
  • 16
  • 16
0

sizeof(vertices) and sizeof(normals) give you 4 both, because vertices and normals are pointers (not arrays). You should use trisize * sizeof(GLfloat) for all glBuffer* commands.

Also, for glDrawArrays you should specify the number of vertices (=triangles*3).

Also, the last 2 commands should look like this for your case:

glVertexPointer(3,GL_FLOAT,0,NULL)
glNormalPointer(GL_FLOAT,0,(void*)(trisize * sizeof(GLfloat)));

Conclusion: you've made many mistakes in a relatively simply piece of code. You didn't read the basic documentation properly. Perhaps, this question should be closed.

kvark
  • 5,291
  • 2
  • 24
  • 33