PDA

View Full Version : How to draw the cal3D model with VBO?



Ehsan Kamrani
01-13-2007, 11:50 PM
Hi
Cal3D uses from the following algorithm during the rendering process to get the information of the model:


for all the meshes
for all the sumbemeshes
{
1)get the information of the vertices, normals and texture coordinates as well as vertex, normal and texture coordinate count
2)Draw the submesh with vertex arrays

Vertex data and other data are saved in a 2D array.As an example:
static GLfloat meshVertices[3000][3];
int vertexCount = pCalRenderer->getVertices(&meshVertices[0][0]);

I have no problem with vertex arrays. I draw in this manner:


static GLfloat meshVertices[3000][3];
int vertexCount = pCalRenderer->getVertices(&meshVertices[0][0]);

// get the transformed normals of the submesh
static GLfloat meshNormals[3000][3];
int normalCount = pCalRenderer->getNormals(&meshNormals[0][0]);

// get the texture coordinates of the submesh
static GLfloat meshTextureCoordinates[3000][2];
int textureCoordinateCount = pCalRenderer->getTextureCoordinates(0, &meshTextureCoordinates[0][0]);
// get the faces of the submesh
static CalIndex meshFaces[3000][3];
int faceCount = pCalRenderer->getFaces(&meshFaces[0][0]);

// set the vertex and normal buffers
glVertexPointer(3, GL_FLOAT, 0, &meshVertices[0][0]);
glNormalPointer(GL_FLOAT, 0, &meshNormals[0][0]);

if( (pCalRenderer->getMapCount() > 0) && textureCoordinateCount > 0 )
{
lightModel.EnableSeparateSpecular();
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_COLOR_MATERIAL);

// set the texture id we stored in the map user data
glBindTexture(GL_TEXTURE_2D, (GLuint)pCalRenderer->getMapUserData(0));

// set the texture coordinate buffer
glTexCoordPointer(2, GL_FLOAT, 0, &meshTextureCoordinates[0][0] );
glColor3f(1.0f, 1.0f, 1.0f);
}
if(sizeof(CalIndex)==2)
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, &meshFaces[0][0]);
else
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_INT, &meshFaces[0][0]);

However with VBOs I only see part of the object:


if( GLEW_ARB_vertex_buffer_object )
{
glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBO_buffer );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, ( 3 * ( vertexCount + normalCount ) + ( 2 * textureCoordinateCount ) )* sizeof( GLfloat ),
NULL, GL_DYNAMIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 3 * vertexCount * sizeof( GLfloat ), meshVertices );
glBufferSubData( GL_ARRAY_BUFFER_ARB, 3* vertexCount * sizeof( GLfloat ), 3 * normalCount * sizeof( GLfloat ), meshNormals );
glVertexPointer( 3, GL_FLOAT, 0, 0 );
glNormalPointer( GL_FLOAT, 0, BUFFER_OFFSET( 3 * vertexCount * sizeof( GLfloat ) ) );
if( (pCalRenderer->getMapCount() > 0) && textureCoordinateCount > 0 )
{
lightModel.EnableSeparateSpecular();
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_COLOR_MATERIAL);

// set the texture id we stored in the map user data
glBindTexture(GL_TEXTURE_2D, (GLuint)pCalRenderer->getMapUserData(0));

// set the texture coordinate buffer
glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET( 3*( vertexCount + normalCount ) * sizeof( GLfloat )) );
glColor3f(1.0f, 1.0f, 1.0f);
}
glDrawArrays( GL_TRIANGLES, 0, 3 );
}
So what's the problem?
Has it worth to use glBufferData in each loop?( deleting the previous data and reallocation the buffer)
-Ehsan-

jide
01-14-2007, 06:26 AM
If parts of the mesh are missing, this generally means you have forgotten some things around.

Why you use glDrawArrays (GL_TRIANGLES, 0, 3) ? and why not using DrawElements as for non VBO arrays ?

Both your codes should be almost the same but the *Buffer* functions and the *Pointer.

Also you should use gl*PointerEXT instead of the default ones. They need the size of the array in additions to the default ones. Use them for both.

No don't use BufferData in each loop !! Just allocate one buffer once for all (for each model). Then BufferSubData each time your data are changing.

I actually don't see any other bad things. If it still doesn't solve your problem, then send or mail more code portion so that I can see how they are used in whole.

Hope that helps.

Ehsan Kamrani
01-15-2007, 12:59 AM
Do I need to change my VBO code if I use glDrawElements() instead of glDrawArrays()?



Also you should use gl*PointerEXT instead of the default ones. They need the size of the array in additions to the default ones. Use them for both.
Can you give me an example of gl*PointerEXT()?



No don't use BufferData in each loop !! Just allocate one buffer once for all (for each model). Then BufferSubData each time your data are changing.
Regarding the algorithm:


for all the meshes
for all the sumbemeshes
{
1)get the information of the vertices, normals and texture coordinates as well as vertex, normal and texture coordinate count
2)Draw the submesh with vertex arrays
}I have 3 options:
1)Allocate the buffer for each submesh once( Assume that I have 20 submeshes and 10 meshes. So I need to generate 20 * 10 buffers!
2)I use from one buffer and reallocate it in each submesh
3)I find the size of the biggest submeh, generate a buffer for it and use this buffer for all the submeshes( Is it possible?)
What selection is better?
-Ehsan-

jide
01-15-2007, 03:53 AM
From the code you gave and from what I just have read from you:

The algorithm you stippled seems poor and bad.

For all the models that always have the same number of polygons create and allocate one vbo for each of them. For all of those which are static models create static draw vbo. For all of those which are dynamic create dynamic draw vbo.

For all the models that do not always have the same number of polygons (are you sure you have some ?) create dynamic vbo with a size capable to handle the maximum of polygons. Do it for each of the models.

You use VBO and you use shaders. So the graphic cards you aim are quiete recent, so they have enough memory for such a 'design'. Allocating a single VBO several times for each frame is really not a good idea. This is the same thing if you have to reallocate each frame. Allocation and deallocation can be quiete fast but are still a serious point of slow downs. So use as much VBO as you have models.

Now back to your algorithm, I guess it should more look like this:

for all the models
{
create a vbo (static or dynamic with enough amount of memory)
keep the vbo id
for all the meshes
for all the submeshes
{
1)get the information of the vertices, normals and texture coordinates as well as vertex, normal and texture coordinate count
2)Copy the data into the vbo buffer
}
}

... later on during rendering

for all the models
{
apply the matching transformation matrix
bind to the matching vbo id
render
}

Use DrawArrays whenever you don't use any index array. Use DrawElements if you have an index array.

glVertexPointerEXT (3, GL_FLOAT, 0, nb_of_elements, 0); // the last 0 here means it's the first element of the vbo bound buffer

Ehsan Kamrani
01-15-2007, 06:42 AM
Regarding the book "More OpenGL Game Programming" :
Buffer objects can also be used to store the indices for glDrawElements(). When calling glBindBuffer(), glBufferData() and glBufferSubData(), GL_ELEMENT_ARRAY_BUFFER is used instead of GL_ARRAY_BUFFER.When any of the variants of glDrawElemets() is called, if a nonzero buffer is bound to GL_ELEMENT_ARRAY, it will be used for the source of the indices.The indices parameter acts as an offset into the indices in the buffer.

Unfortunately, there's no example about this topic.So what's the meaning of this sentence :
When any of the variants of glDrawElemets() is called, if a nonzero buffer is bound to GL_ELEMENT_ARRAY, it will be used for the source of the indices.The indices parameter acts as an offset into the indices in the buffer.

Does it mean I should change this line when working with VBOs:
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, &meshFaces[0][0]);
-Ehsan-

jide
01-15-2007, 11:34 AM
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, 0);

should do the thing. It's just like vertex arrays.

Ehsan Kamrani
01-19-2007, 09:19 PM
I replaced all the GL_ARRAY_BUFFER constants with GL_ELEMENT_ARRAY_BUFFER. But where should I specify the indexes?
The array consisting of the indexes is specified with glDrawElements() when using the original vertex arrays. What about the VBOs?
-Ehsan-

jide
01-20-2007, 02:53 AM
Don't replace all of them. Replace only where you stipple the index array. And use the call to glIndexPointer when you bind to the GL_ELEMENT_ARRAY_BUFFER VBO.

For the call, as I said in my previous post, you should call DrawElements with the last parameter set to the offset of the index array set on the graphic card memory (probably 0).

Ehsan Kamrani
01-20-2007, 05:54 AM
I changed the code( I have not yet optimized it, all the code is inside the rendering loops ). The problem is that the last line of this code generates a run time error. Here's the code:


if( GLEW_ARB_vertex_buffer_object )
{
glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBO_buffer );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, ( 3 * ( vertexCount + normalCount ) + ( 2 * textureCoordinateCount ) )* sizeof( GLfloat ) + 3 * faceCount * sizeof( CalIndex ),
NULL, GL_DYNAMIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 3 * vertexCount * sizeof( GLfloat ), &meshVertices[0][0] );
glBufferSubData( GL_ARRAY_BUFFER_ARB, 3* vertexCount * sizeof( GLfloat ), 3 * normalCount * sizeof( GLfloat ), &meshNormals[0][0] );
if( textureCoordinateCount > 0 && (pCalRenderer->getMapCount() > 0))
glBufferSubData( GL_ARRAY_BUFFER_ARB, 3 * ( vertexCount + normalCount ) * sizeof( GLfloat ), 2 * textureCoordinateCount * sizeof( GLfloat ), &meshTextureCoordinates[0][0] );
glBufferSubData( GL_ELEMENT_ARRAY_BUFFER_ARB, 3*( vertexCount + normalCount ) * sizeof( GLfloat ) + 2 * textureCoordinateCount * sizeof( GLfloat ), 3 * faceCount * sizeof( CalIndex ), &meshFaces[0][0] );

glVertexPointer( 3, GL_FLOAT, 0, 0 );
glNormalPointer( GL_FLOAT, 0, BUFFER_OFFSET( 3* vertexCount * sizeof( GLfloat ) ) );

if( (pCalRenderer->getMapCount() > 0) && textureCoordinateCount > 0 )
{
lightModel.EnableSeparateSpecular();
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_COLOR_MATERIAL);

// set the texture id we stored in the map user data
glBindTexture(GL_TEXTURE_2D, (GLuint)pCalRenderer->getMapUserData(0));

// set the texture coordinate buffer
glTexCoordPointer(2, GL_FLOAT, 0, BUFFER_OFFSET( 3*( vertexCount + normalCount ) * sizeof( GLfloat )) );
glColor3f(1.0f, 1.0f, 1.0f);
}

if(sizeof(CalIndex)==2)
glIndexPointer( GL_SHORT, 0, BUFFER_OFFSET( 3 * ( vertexCount + normalCount ) * sizeof( GLfloat ) + 2 * textureCoordinateCount * sizeof( GLfloat ) ) );
else
glIndexPointer( GL_INT, 0, BUFFER_OFFSET( 3 * ( vertexCount + normalCount ) * sizeof( GLfloat ) + 2 * textureCoordinateCount * sizeof( GLfloat ) ) );

// draw the submesh
if(sizeof(CalIndex)==2)
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET( 3 * ( vertexCount + normalCount ) * sizeof( GLfloat ) + 2 * textureCoordinateCount * sizeof( GLfloat ) )) );
else
glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_INT, BUFFER_OFFSET( 3 * ( vertexCount + normalCount ) * sizeof( GLfloat ) + 2 * textureCoordinateCount * sizeof( GLfloat ) ) );
}-Ehsan-

jide
01-21-2007, 01:34 AM
You need 2 vbos, one for the vertex, normal, color and texcoord array and one for the index array.


glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBO_buffer );
glBufferDataARB( GL_ARRAY_BUFFER_ARB, ( 3 * ( vertexCount + normalCount ) + ( 2 * textureCoordinateCount ) )* sizeof( GLfloat )), NULL, GL_DYNAMIC_DRAW );
// copy data with BufferSubData

glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, VBO_index_buffer );
glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, (3 * faceCount * sizeof( CalIndex ),NULL, GL_DYNAMIC_DRAW );
// copy data with BufferSubData

glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBO_buffer );
glVertexPointer( 3, GL_FLOAT, 0, 0 );
glNormalPointer( GL_FLOAT, 0, BUFFER_OFFSET( 3* vertexCount * sizeof( GLfloat ) ) );

glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, VBO_index_buffer );
glIndexPointer( GL_SHORT, 0, 0);

glDrawElements(GL_TRIANGLES, faceCount * 3, GL_UNSIGNED_SHORT, 0);should do the work.

EDITED !! changed things in the code... Sorry for that point.

Ehsan Kamrani
01-21-2007, 04:59 AM
I changed the code. Now the shapes are drawn correctly with VBOs. But I can not see a major difference in the FPS( I disabled VSYNC to get the actual FPS ). I guess it is because of reloading the VBO buffer for each submesh. Isn't it?
-Ehsan-

jide
01-21-2007, 05:38 AM
You mean you don't see any difference in the FPS regarding before you used index arrays in vbos ? It 'can' be normal. Things depend on many factors. Or do you mean something else ?

If you use BufferData for each submesh and for every frame, then it could have an impact yes.

Ehsan Kamrani
01-21-2007, 06:42 AM
Yes I mean I can't see any differences in FPS before using VBOs and after using VBOs. Note that I removed glIndexPointer() from the code. It had no effect in output.
-Ehsan-