OpenGL 3.3 VBOs and VAOs.. Why do my models not render properly?

Started by
13 comments, last by BitMaster 11 years, 5 months ago
Okay, I need some wisdom from this wonderful place, thankyou please!

Here's the deal: I wrote (based on Ranger_Ones method) a loader for .obj files. It works well! I am happy. However, we live in a world of 3.3 core these days, and so the Immediate Mode rendering I was doing is now not good enough, so we turn to VAOs and VBOs.

I'm familiar with the idea of how all this works, but not the implementation. I've had a few forays into looking this up, and I've got this far thanks to rastertek (once again), my computer graphics tutor, and the SuperBible and Beginning Game... 2 with.OpenGL.

Enough history! Time for some code, because it's easier to see how it worked. In fact.. I should proably take this time to apologize for posting such a lot of code. Sorry, dude.

This is how I render my client/cpu-side -stored arrays with immediate mode, atm:




//------------------elsewhere-----------------------------------------
struct Vertex3D { float x, y, z; }; typedef Vertex3D Normal3D; struct UV { float u, v;};

class Mesh { //we could assume mesh/model looks like this for clarity
Vertex3D * vertices;
Normal3D * normals;
UV * uvs;

/* for VBO, VAO functionality */
GLuint meshVAO;
GLuint meshVertexBuffer;
GLuint meshNormalBuffer;
GLuint meshTexCoordBuffer;
GLuint meshIndexBuffer;
GLuint meshColB; //? Unused


} //etc
//----------------------------------------------------------
glBegin(GL_TRIANGLES);
for ( int i = 0; i < mesh.noTriangles; i++ ) { //for each triangle in the list
Vector3D vpos; Normal3D npos;
for( int j = 0; j < 3; j++ ) {
// this vertex is the said triangle, verts attached
vpos.x = mesh.vertices[ mesh.triangleList.Vertex[j] ].x;
vpos.y = mesh.vertices[ mesh.triangleList.Vertex[j] ].y;
vpos.z = mesh.vertices[ mesh.triangleList.Vertex[j] ].z;
// this vertex is the said triangle, normals attached
npos.x = mesh.normals[ mesh.triangleList.Normal[j] ].x;
npos.y = mesh.normals[ mesh.triangleList.Normal[j] ].y;
npos.z = mesh.normals[ mesh.triangleList.Normal[j] ].z;

//PS SOMETIMES TEX COORDS TOO
glNormal3f(npos.x, npos.y, npos.z);
glVertex3f(vpos.x, vpos.y, vpos.z);
} //j++
} // i++
glEnd();



And Now I'd like to do it with VBOs and a VAO per model.. So wheres the error in my setup thats causing my model not to render properly?

I've loaded a model into memory, I'm not going to edit it, but I'd like my RAM back.. and so STATIC_DRAW is k for now..


void GLModel::LoadBuffers() {
//if (glGenVertexArrays)
// cout << "okay" << endl;
glGenVertexArrays(1, &meshVAO); //Allocate an OpenGL VAO
glBindVertexArray(meshVAO); //Bind the VAO to store all the buffers and attributes we create here

//position data
glGenBuffers(1, &meshVertexBuffer); //Generate an ID for the Vertex Buffer
glBindBuffer(GL_ARRAY_BUFFER, meshVertexBuffer); //Bind the Vertex Buffer and load the vertex (position, normal, texcoord data) into the Vertex Buffer
glBufferData(GL_ARRAY_BUFFER, sizeof( Vertex3D ) * mesh.noVerts, mesh.vertices, GL_STATIC_DRAW);
glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (const GLvoid * ) 0 ); //attribute 0 gets vertex position data
/*
(const GLvoid * ) 0 refers to a pointer.. a pointer to where? or size of pointer? an offset?
*/
// normal data
glGenBuffers(1, &meshNormalBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshNormalBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof( Normal3D ) * mesh.noNormals, mesh.normals, GL_STATIC_DRAW);
glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, sizeof(Normal3D), (const GLvoid * ) 0 ); //attribute 1 gets normal data
// texcoord data
glGenBuffers(1, &meshTexCoordBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshTexCoordBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof( UV ) * mesh.noTexCoords, mesh.texcoords, GL_STATIC_DRAW);
glVertexAttribPointer( 2, 2, GL_FLOAT, GL_FALSE, sizeof(UV), (const GLvoid * ) 0 ); //attribute 2 gets UV data
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);

// Faces / Indices
glGenBuffers(1, &meshIndexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIndexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof( Triangle ) * mesh.noTriangles, mesh.triangleList, GL_STATIC_DRAW);

glBindVertexArray(0); //done binding to meshVAO
}
Advertisement
try moving the glEnableAttribArray() to before you bind another vbo, and possibly enable that index for ONLYthat vbo =)
i think that's what's happening here

try moving the glEnableAttribArray() to before you bind another vbo, and possibly enable that index for ONLYthat vbo =)
i think that's what's happening here


I will let you know in the 'morrow friend! Thanks for your advice!
Now this may help Learning Modern 3D Graphics Programming (OpenGL 3.3).

Aaand this may help too glVertexAttribPointer (spoiler: last parameter is an offset).

All OpenGL functions are documented in OpenGL.org so as long you have internet, you can know more or less what each one does :D

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"

My journals: dustArtemis ECS framework and Making a Terrain Generator

Hi Guys.

I tried moving the glVertexAttribArrayPointer( n ) to after the buffer creation for each buffer (vertices, normals, texcoords etc), but the effect is the same.

It's been annoying me not knowing what the last pointer for glVertexAttribArray was, and you know, I wondered if it was an offset! Thing is there is no offset - each buffer is created from a seperate array..? Thanks though TheChubu, I'll be using that resource often methinks! Also, I think I've come across that 1st link before - also good, so thank you!


and possibly enable that index for ONLYthat vbo =)


..can you nudge me a bit harder please? Thanks!

I'm wondering if it's my render-time code? The set up at init time seeeeeems to be fine. Heres the render code (by the by, I'm not sure all of this is necessary):

void GLModel::RenderElements() {
glPushMatrix();
glTranslatef(pos.x, pos.y, pos.z);
glColor3f( 0.0f, 1.0f , 0.0f );
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);
glBindVertexArray( meshVAO );
glDrawElements(GL_TRIANGLES, mesh.noTriangles * 9, GL_UNSIGNED_INT, (const GLvoid*) 0);
glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glPopMatrix();
}


Thanks again guys!
It doesn't look like you're using shaders to draw, but are trying to use generic vertex attributes (with glVertexAttribPointer). the first argument to glVertexAttribPointer is supposed to be an attribute index in the shader program you plan to draw with, that you either bound before linking said program or retrieved via glGetAttribLocation after. I believe that on some hardware the way you're doing it (by mapping position to 0, normal to 1, and tex coords to 2) might work, but I wouldn't trust it.

If you just want to get what you have now working without shaders, try using glVertexPointer, glNormalPointer, and glTexCoordPointer in lieu of glVertexAttribPointer, and replace glEnableVertexAttribArray with glEnableClientState (call it 3 times, passing one of GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_VERTEX_ARRAY each time)

Not to nitpick, but 3.3 core has deprecated the matrix stack as well. If you want to bring your code completely "up to date", I would recommend handling matrices yourself or with a library, making use of shaders for drawing geometry, and also using generic attribute buffers (glVertexAttribPointer and the like).
you can take it step by step, but as the above person is telling you, start using shaders immediately
then we'll help you with everything else once you know for certain that your shaders are loaded and linked properly!

if you are doing everything manually, as it looks like you're doing, then you should be able to find small functions to load and link shaders easily enough
check and double check that the shaders are loaded and without errors, then call 911-GAMEDEV again :)
The problem was indeed indexing, I've now got it working beautifully:

thing is struct Triangle held 9 ints, not 3.. (immediate mode relic - now all gone).
So I've replaced with a struct Face that holds only vertices indices, which is the index used for all three arrays on the GPU - tex coords, normals, and verts.

One nested for loop later before I bind any buffers and I'm good to go.

glBindVertexArray( meshVAO );
glDrawElements(GL_TRIANGLES, mesh.noTriangles * 3, GL_UNSIGNED_INT, (const GLvoid*) 0);


And next I'm doing shaders - hence the setup above - this will be a fully textured, shaded, properly lit object in a few weeks..
Come March it will be .3DS, animated, instead of .obj too.. Wish me luck!


then you should be able to find small functions to load and link shaders easily enough
check and double check that the shaders are loaded and without errors, then call 911-GAMEDEV again


Indeed - a wrapper for the shaders is next on the list. Ahh shaders.. how I love thee!

..Also a few more preliminaries like a decent camera class, a wrapper for lights, implement DirectInput, etc etc
My tutor has read this thread, and he agrees with Koehler's consensus. As do I, it just took a while to sink in. Apologies guys!

It's a bit messy - this whole project - because I'm trying to rewrite something that was written for FFP and redirect it for 3.3 core, and perhaps some GLES (but forget I even mentioned that for now).

So my draw method should look like this, given that m_Program is a compiled, linked, shader program..?


void GLModel::RenderElements() {
glUseProgram( m_Program );
glTranslatef(pos.x, pos.y, pos.z);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDisable(GL_CULL_FACE);

glBindVertexArray( meshVAO );
glDrawElements(GL_TRIANGLES, mesh.noTriangles * 3, GL_UNSIGNED_INT, (const GLvoid*) 0);

glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glUseProgram( 0 );
}


I'll obviously have to set some uniforms somewhere? _ I can figure that out I'm sure.

Thanks again guys. :)


the first argument to glVertexAttribPointer is supposed to be an attribute index in the shader program you plan to draw with, that you either bound before linking said program or retrieved via glGetAttribLocation after. I believe that on some hardware the way you're doing it (by mapping position to 0, normal to 1, and tex coords to 2) might work, but I wouldn't trust it.

If you just want to get what you have now working without shaders, try using glVertexPointer, glNormalPointer, and glTexCoordPointer in lieu of glVertexAttribPointer, and replace glEnableVertexAttribArray with glEnableClientState (call it 3 times, passing one of GL_NORMAL_ARRAY, GL_TEXTURE_COORD_ARRAY, GL_VERTEX_ARRAY each time)

Not to nitpick, but 3.3 core has deprecated the matrix stack as well. If you want to bring your code completely "up to date", I would recommend handling matrices yourself or with a library, making use of shaders for drawing geometry, and also using generic attribute buffers (glVertexAttribPointer and the like).


Shaders please!
Okay..
So the first attribute of glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (const GLvoid * ) 0 );
refers to, in the shader file, somethin like "layout vec3 (location = 0) in VxPos;" where location is the first paramater of glVertexAttribPointer?

Deprecated Matrix Stack - indeed!
This has been bugging me no end. Is something like float some_matrix[16]; really the best way to represent a matrix? Would something like some_matrix[4][4] be more or less efficient? I imagine the code would be a bit nicer..?

Generic Attribute Buffers?

For what its worth, the nitpicking is good, dude! Always, always helpful - I'm learnig a lot here! :)

So my draw method should look like this, given that m_Program is a compiled, linked, shader program..?
(snip)


That looks correct! Just remember that if you're drawing later on with glPolygonMode(GL_FRONT,GL_FILL), you might as well re-enable GL_CULL_FACE. It should not affect your visual results and should improve performance. When you're ready to add uniforms, you'll pass them in your GLModel::RenderElements() function.

In C++, the choice between matrix[16] and matrix[4][4] comes down to which you like better. matrix[1][1] in the second is the same offset as matrix[4] in the first. The important thing to remember for openGL is that it is column-major in its memory layout (so array element 0 (or [0][0]) is row 0, column 0, but element 1 (or [0][1]) is row 1, column 0. Fortunately, if you want to access your 2D array as matrix[row][column], you can tell OpenGL to transpose it when you pass it in as a uniform (third argument to any of the glUniformMatrix_fv functions).

That said, it's probably easiest to work with a Matrix class so that you can do things like this:

mat4x4 viewProjectionMatrix = projectionMatrix*viewMatrix;
.
note that in shader code vectors and matrices are pre-defined types, so code like the above would work.

You may notice that I've referred to "ViewProjection" matrix above, whereas OpenGL has stacks for the "ModelView", and "Projection" matrices. As you move away from using the matrix stack, the easiest way to think of this is as three separate matrices, Model ,View, and Projection. Under the hood, it's effectively doing this:

vec4 finalVertexPos = Projection*View*Model*vertexPos;


For convenience's sake, since the camera doesn't change over the course of a single frame, I like to combine the view and projection matrices together just once, and use the result like this:

vec4 finalVertexPos = viewProjection*Model*vertexPosition;


I'm a bit short on time to explain how to generate view and projection matrices. For a temporary solution you can use glGetFloatv with GL_MODELVIEW_MATRIX or GL_PERSPECTIVE_MATRIX to extract the ones you're already making with the matrix stack (exclude your last glTranslate if you want just your view matrix)

Warning: glGet calls are comparatively slow and you are far better off generating your own, but for comparison's sake this will give you matrices that you can start with.

One step at a time is best.
First focus on getting a shader to compile, using view/projection matrices you copied out of the stack with glGetFloatv, and replacing your glTranslate with a glUniform3f() to move your object around. This will give you a working set of values to verify your shader functions with.
Once that's done, start building (or just download one that already exists) a matrix math library, and work on building full model transforms (translation*rotation*scale) as well as your own view/projection calculations.


Generic Attribute Buffers?

That was me not properly referring to VAO's. You've got them already, so this is done :)

This topic is closed to new replies.

Advertisement