1
$\begingroup$

How to convert from glBegin() and glEnd() to VBOs (Vertex Buffer Objects)?

I found a simple example here:

GLfloat vertices[] = {...}; // 36 of vertex coords
...
// activate and specify pointer to vertex array
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);

// draw a cube
glDrawArrays(GL_TRIANGLES, 0, 36);

// deactivate vertex arrays after drawing
glDisableClientState(GL_VERTEX_ARRAY);

http://www.songho.ca/opengl/gl_vertexarray.html


So if I have code in very "standard" glBegin() and glEnd() fashion like:

glBegin(GL_TRIANGLES);

    for(int j = 0; j < vecf.size(); j++){
        glNormal3f(vecn[vecf.at(j)[2]-1][0], vecn[vecf.at(j)[2]-1][1], vecn[vecf.at(j)[2]-1][2]);
        glVertex3f(vecv[vecf.at(j)[0]-1][0], vecv[vecf.at(j)[0]-1][1], vecv[vecf.at(j)[0]-1][2]);
        glNormal3f(vecn[vecf.at(j)[5]-1][0], vecn[vecf.at(j)[5]-1][1], vecn[vecf.at(j)[5]-1][2]);
        glVertex3f(vecv[vecf.at(j)[3]-1][0], vecv[vecf.at(j)[3]-1][1], vecv[vecf.at(j)[3]-1][2]);
        glNormal3f(vecn[vecf.at(j)[8]-1][0], vecn[vecf.at(j)[8]-1][1], vecn[vecf.at(j)[8]-1][2]);
        glVertex3f(vecv[vecf.at(j)[6]-1][0], vecv[vecf.at(j)[6]-1][1], vecv[vecf.at(j)[6]-1][2]);
    }

glEnd();

Then how do I convert this to VBOs?


Particularly, I already know that I need to supply vertices array and normals array, but I seem to confuse the orders of some vecs, because what's drawn becomes out as distorted.

$\endgroup$
2
$\begingroup$

Back when I did the transition from immediate mode to vertex buffer object (VBO) in my own code base, I have found it easier to proceed with these steps:

From naive immediate mode code...

The original code was something like this:

glBegin(GL_TRIANGLES);
glNormal3f(nx, ny, nz);
glVertex3f(px, py, pz);
// [...] arbitrary long sequence of such function calls.
glEnd();

1. To array based immediate mode code...

First, keep using the immediate mode, but make sure that all values passed to the vertex functions come from an array. This seems to be already the state of the code described in the question.

float vertexData[] = { ... }; // Can be all together, or split by attribute type, as you prefer.
glBegin(GL_TRIANGLES);
for (int i = 0; i < numberOfVertices; ++i)
{
    // The indices are going to depend on the data layout.
    glNormal3f(vertexData[0], vertexData[1], vertexData[2]);
    glVertex3f(vertexData[3], vertexData[4], vertexData[5]);
}
glEnd();

2. To vertex array version...

Second, you can replace the loop by a handful of calls with vertex arrays.

glEnableClientstate(GL_Vertex_Array);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), vertexData);
glNormalPointer(GL_FLOAT, 6 * sizeof(float), vertexData + 3);
glDrawArrays(GL_TRIANGLES, 0, numberOfVertices);
glDisableClientState(GL_VERTEX_ARRAY);

3. To vertex buffer version

Finally from the vertex array based version, the transition to VBO is minimal:

// An initialization part will create and populate the vertex buffer:
GLuint vboId = 0;
glGenBuffers(1, &VBOid);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOid);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(float), vertexData, GL_STATIC_DRAW);


// A draw part will bind the vertex buffer and issue the drawcall.
// It's almost the same code as before, except:
// - We bind a VBO
// - We pass (0+offset) as the last parameter for the gl*pointer().
glBindBufferARB(GL_ARRAY_BUFFER_ARB, VBOId);

glEnableClientstate(GL_Vertex_Array);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (void*)(0));
glNormalPointer(GL_FLOAT, 6 * sizeof(float), (void*)(0 + 3));
glDrawArrays(GL_TRIANGLES, 0, numberOfVertices);
glDisableClientState(GL_VERTEX_ARRAY);

And that's it. Both vertex array and VBO allow to work directly with vertices or with vertex index, so be careful about possible confusion there.

P.S.: I have written this answer without double checking the code, so it's probably ridden with typos and not meant for copy-pasting. I invite you to read the documentation, understand the API and refer to other examples when you implement it. At first, VBO can be really annoying to get to work properly.

I have used the same distinction when answering this other question about performance of VBO.

$\endgroup$
0
$\begingroup$

You first need to put all the data into a VBO in the correct order, it's important that each vertex position corresponds with the normal it uses.

struct Vertex{
    vec3 pos;
    vec3 normal;
}

std::vector<Vertex> vertices;
vertices.reserve(vecf*3);
Vertex v;

for(int j = 0; j < vecf.size(); j++){
    v.normal = vec3(vecn[vecf.at(j)[2]-1][0], vecn[vecf.at(j)[2]-1][1], vecn[vecf.at(j)[2]-1][2]);
    v.pos = vec3(vecv[vecf.at(j)[0]-1][0], vecv[vecf.at(j)[0]-1][1], vecv[vecf.at(j)[0]-1][2]);
    vertices.push_back(v);
    v.normal = vec3(vecn[vecf.at(j)[5]-1][0], vecn[vecf.at(j)[5]-1][1], vecn[vecf.at(j)[5]-1][2]);
    v.pos = vec3(vecv[vecf.at(j)[3]-1][0], vecv[vecf.at(j)[3]-1][1], vecv[vecf.at(j)[3]-1][2]);
    vertices.push_back(v);
    v.normal = vec3((vecn[vecf.at(j)[8]-1][0], vecn[vecf.at(j)[8]-1][1], vecn[vecf.at(j)[8]-1][2]);
    v.pos = vec3(vecv[vecf.at(j)[6]-1][0], vecv[vecf.at(j)[6]-1][1], vecv[vecf.at(j)[6]-1][2]);
    vertices.push_back(v);
}

int vbo;
glGenBuffer(1, &vbo);

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*vertices.size(), vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(posAttr, 3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, pos));
glVertexAttribPointer(normalAttr, 3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, normal));

then you can draw it with glDrawArrays(GL_TRIANGLES, 0, vertices.size());

$\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.