PDA

View Full Version : Trying to use Vertex Arrays and Buffer Objects



arnaugm
09-10-2007, 11:10 AM
Hello,
I'm trying to improve a simple aplication used to visualize 3ds models. I coded with the glBegin()/glEnd() paradigm but I've read that it is not a good method.
I read then about the use of Vertex Arrays and Buffer Objects but I've been having problems with the coding.

Till now the algorithm was reading the data structure of the 3ds model and sending the triangles one by one like this:

glBegin(GL_TRIANGLES);
glVertex3fv(.....);
glEnd();If I have understood well, now the idea is to put all these vertex into a Vertex Array at the moment of loading the model and in the paintGL() function, use glDrawElements() or glDrawRangeElements() or glMultiDrawElements().

When loading the model I have this:

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
//For each triangle
glVertexPointer(3, GL_FLOAT, 0, vertex);
numTriangles++;And then, in paintGL:

glDrawRangeElements(GL_TRIANGLES, 0, numTriangles - 1, numTriangles, GL_UNSIGNED_SHORT, ???);The first thing that I don't understand is which version of the glDrawElements function is the most appropriate in this case, and the second is what this last parameter "const GLvoid *indices" refers to.

Can anybody clarify my mind?
Thankyou.

Lindley
09-10-2007, 02:02 PM
Well, first of all, understand how Vertex Buffer Objects extend the notion of Vertex Arrays. They merely allow the pointer arguments of vertex array calls to point to GPU memory rather than system memory. So far, you've got no VBO code there, only VA code.

Second, "indicies" indicates which of the elements in the array you'd like to draw. If you want them all, consider using glDrawArrays instead. I've heard it's a bit slower, but for starting out it will work well enough.

arnaugm
09-10-2007, 04:20 PM
I was confused about Vertex Arrays, so I wanted to clarify it before enter to deal with Buffer Objects.
In this case I'll try glDrawArrays.

Anyway, in the case I wanted to draw only some of these elements of the array, I still don't understand how can I guess these indices, because in the moment you build the array with glVertexPointer, it doesn't return any index. Have I to assume that the first vertex have the index 0, the second the index 1, and so on?

And if I need to add information about the normal and the material of the vertex?

Thanks again.

Lord crc
09-10-2007, 07:58 PM
glVertexPointer just tells OpenGL where the vertex data is stored. It's indexed as a normal C array, ie first element is 0, next is 1 etc.

So what you want to do is allocate an array for all your vertexes, fill it with the data you want, and then call glVertexPointer once, passing the address of the array.

I suggest you read the "Vertex Arrays" chapter in the OpenGL specifications, available at http://www.opengl.org/documentation/specs/
It explains it rather well in my opinion.

As mentioned, VBO is just a way to store the vertex array "server side" (ie video card/driver), rather than in your program. It doesn't change the fundamentals of how the vertex array stuff works, just where the actual memory "exists".

Lindley
09-10-2007, 08:02 PM
You can use glNormalPointer, glColorPointer, and glTexCoordPointer to do some per-vertex things. However, I don't think there's a glMaterialPointer; if you want to do that, then either (a) have a separate VA for each material and just specify it beforehand, or (b) use a Cg vertex program with a Cg pointer parameter to bring in the extra properties. I don't actually know what Cg semantics you'd use here, so maybe it's best to just go with (a).

With regards to indices, they should be fairly obvious. The gl*Pointer calls specify precisely how OpenGL should interpret the memory you're pointing to. The "pointer" param initially points to index 0; the "stride" parameter tells OGL how many bytes away the next index is.

There is one subtlety. "Stride" has a special case if it's 0---it assumes that "type" and "size" then define the stride. However, do *not* interpret this to mean that "stride" measures the number of byes between indices. Thus,
glVertexPointer(4,GL_FLOAT,16,ptr)
is exactly equivalent to
glVertexPointer(4,GL_FLOAT,0,ptr)

Do not be confused by glIndexPointer. That's for color-index mode, *not* for indexing into a VA.

arnaugm
09-11-2007, 05:01 AM
I'll try these options and tell how does it go.
Thanks for your help.

arnaugm
09-12-2007, 03:11 AM
And how I define this separate VA for materials, with VertexAttribPointer?

arnaugm
09-12-2007, 07:17 AM
Meanwhile I've tried to use Buffer Objects.
Compiling I get the error "glGenBuffers was not declared in this scope", but the result of calling glGetString(GL_VERSION) is "2.1.0 NVIDIA 96.31".
It's maybe a missing include?

Lindley
09-12-2007, 09:20 AM
Are you including GL/glew.h before GL/gl.h? (Or GLee.h, if you're using that instead.)

I know it's not supposed to be an extension in 2.0, but it doesn't hurt to always bring in available extensions. And you can always try using glGenBuffersARB if nothing else works; it should do the same.

I haven't used glVertexAttribPointer, but I suspect you could use it for material properties if you wished. I was more thinking that each object which had different properties would be represented by a different VA, and you'd just call glMaterial() between each glDrawArrays().

Lord crc
09-13-2007, 07:20 PM
Originally posted by arnaugm:
And how I define this separate VA for materials, with VertexAttribPointer? Just be aware that some attributes might alias with "built in" attributes. For instance, attribute 0 is (afaik always, possibly only "sometimes") the position. Read the documentation to be certain.

arnaugm
09-17-2007, 07:05 AM
Again with the Vertex Arrays.
I suppose it's a problem constructing the array but it's strange that a part of the model appears correct and the rest simply doesn't appear.

Here I show you my problem:

Simple geom drawn with glBegin()/glEnd()
http://www-est.fib.upc.edu/~e6976898/cap1.png

Using Vertex Arrays
http://www-est.fib.upc.edu/~e6976898/cap2.png

A dolphin drawn with glBegin()/glEnd()
http://www-est.fib.upc.edu/~e6976898/cap3.png

The dolphin with VA
http://www-est.fib.upc.edu/~e6976898/cap4.png

Lord crc
09-18-2007, 12:39 PM
Very hard to say anything without code, but you could try to make your own glDrawElements or similar. It should accept the same data you "feed" opengl with, but use glBegin/glEnd. It's just a few lines of code and will help narrow down where the problem is.

Zengar
09-18-2007, 01:41 PM
Fine some decent tutorial on vertex array (this page may be a good start http://nehe.gamedev.net/ ) and work your way through. It is always better to learn things step by step and not rush in. There could be so many things you got wrong in your examples: from setting bad array pointers to using bad arguments with DrawArrays/DrawElements functions...

arnaugm
09-19-2007, 06:49 AM
I suspected a typical mistake using Vertex Arrays, for this reason I didn't add any code.

The strange thing is the code is the same than using glBegin()/glEnd() but putting the vertex in the array instead of sending them with glVertex().

At loading time the code goes as follows:

void glvisor::generateVertexArrays() {

GLfloat arrayVertexs[numTriangles * 3 * 3];
GLfloat arrayNormals[numTriangles * 3 * 3];

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);

indexAV = 0;
addVertexs(arrayVertexs, arrayNormals);
glVertexPointer(3, GL_FLOAT, 0, arrayVertexs);
glNormalPointer(GL_FLOAT, 0, arrayNormals);

} // generateVertexArrays

void glvisor::addVertexs(GLfloat *av, GLfloat *an) {

if(!file) return;
Lib3dsNode *n;
for(n = file->nodes; n; n = n->next) {
addVertexsRec(n, av, an);
} // for

} // addVertexs

void glvisor::addVertexsRec(Lib3dsNode *n, GLfloat *av, GLfloat *an) {

for(Lib3dsNode *p = n->childs; p!=0; p = p->next) {
addVertexsRec(p, av, an);
} // for

if(n->type != LIB3DS_OBJECT_NODE || strcmp(n->name, "$$$DUMMY") == 0)
return;

Lib3dsMesh *mesh = lib3ds_file_mesh_by_name(file, n->name);

for(Lib3dsDword i = 0; i < mesh->faces; ++i) {
Lib3dsFace *f = &amp;mesh->faceL[i];

GLfloat normal[3];
GLfloat coords[3];
GLfloat div;
for(int j = 0; j < 3; j++) {
coords[0] = (((Lib3dsPoint *)(mesh->user.i))[f->points[j]].pos[0]);
coords[1] = (((Lib3dsPoint *)(mesh->user.i))[f->points[j]].pos[1]);
coords[2] = (((Lib3dsPoint *)(mesh->user.i))[f->points[j]].pos[2]);

div = sqrt(coords[0] * coords[0] +
coords[1] * coords[1] +
coords[2] * coords[2]);

normal[0] = coords[0] / div;
normal[1] = coords[1] / div;
normal[2] = coords[2] / div;

av[indexAV] = mesh->pointL[f->points[j]].pos[0];
av[indexAV + 1] = mesh->pointL[f->points[j]].pos[1];
av[indexAV + 2] = mesh->pointL[f->points[j]].pos[2];
an[indexAV] = normal[0];
an[indexAV + 1] = normal[1];
an[indexAV + 2] = normal[2];
indexAV += 3;
} // if
} // for
} // for

} // addVertexsRec And the drawing uses:


glDrawArrays(GL_TRIANGLES, 0, numTriangles);

Zengar
09-19-2007, 07:42 AM
I hope you understand that you declare your arrays on stack and they become invalid once the function "generateVertexArrays" returns?

arnaugm
09-19-2007, 08:14 AM
I always have problems with this kind of things.
Why it's drawn partially well then? Anyway.

I thought in declaring the arrays as a global variable but the problem is I don't know how long they must be until the load function takes part and counts the trianges of the model.
How can I do that?

Zengar
09-19-2007, 08:32 AM
Allocate them on heap, that would be probably the best choise. Or use VBOs (which are actually pointers to server-side memory).

Or use a garbage-collected language ;)

arnaugm
09-20-2007, 04:34 AM
Problem solved.

There was certainly a mistake with the declaration of the arrays.

Now I've declared the pointers as members of the class (not as global variables, I said it wrong before) and in the function I get some memory and link it with the pointers.

arrayVertexs = new GLfloat[numTriangles * 3 * 3];
arrayNormals = new GLfloat[numTriangles * 3 * 3]; Apart from that in the glDrawArrays() I used the number of triangles in the last parameter and it needs the number of vertexs. This is why the models were partially drawn.

Sorry for annoying with such stupid problems and thanks for your help.

Lindley
09-20-2007, 07:01 AM
Just don't forget to delete those after you're done with them.

arnaugm
09-20-2007, 07:23 AM
About the materials.

I've analyzed some of my models and there are a few with a lot of different materials, including one with more than 700.
Do you still think it's a good idea to use a different vertex array for each material?

It also needs a preprocess to identify which faces have the same or different material and it slows the application a lot.

If there are special vertex arrays for normals or textures or normal color, why there aren't for materials? There must be an easier way to add this information.

Zengar
09-20-2007, 08:26 AM
The 3ds materials don't really map to (core) OpenGL materials. It all depends of what you want to do... If by "material" you mean different diffuse reflectance, then just glEnable(GL_COLOR_MATERIAL) and use the color array. If it is something more complex (like different reflectance model/different texture), then you will have to do it yourself, using shaders, for example.

arnaugm
09-21-2007, 05:25 AM
I need to use the material properties of each face:

mat->ambient
mat->diffuse
mat->specular
mat->shininess

How I introduce all this information into the array? Because I need one color for each element and here I have three and the shininess.

Zengar
09-21-2007, 05:55 AM
AFAIK, it is not possible with the fixed pipeline. The glColorMaterial allows you to set only one of this parameters via the color array.

Please correct me if I am wrong.

arnaugm
09-21-2007, 05:44 PM
But the combination of these four properties form a final an unic color for each pixel, doesn't it?
And it's not possible to calculate this color and to use in the array?

Zengar
09-22-2007, 03:03 AM
Uhm, this four properties are used in lighting calculation. So the final color of each fragment depend on number of factors like normal cector and position of a fragment, number/position/direction/color of lights etc.
Refer to the specification for more detailed formulas. There is an extension that introduces a secondary color (now part of the core), usually used for a specular component AFAIK, but my experience with fixed pipeline is quite limited, so I may be mistaken. I suggest that you read the specification and see if you find something you could use.

arnaugm
09-28-2007, 04:13 AM
Reading more about the materials issue I've arrived at the conclusion that, as somebody suggested, the only option is to use shaders. Even in some books it's written that the problem with these kind of data structures like VBO is that there isn't enough support for materials by now.

I'm using the color array with the diffuse parameter while I decide if it's necessary or not to deal with shaders.

Thanks for all your suggestions.

Finally I resolved the compilation problem with VBOs adding the line
#define GL_GLEXT_PROTOTYPES
to my header file, even I'm not sure about the reason. I use a 2.1 OpenGL version and VBOs are already in the core.

The includes are

glvisor.h

#define GL_GLEXT_PROTOTYPES
#include <qgl.h>
#include <lib3ds/file.h>
#include <qstring.h>
#include <qpushbutton.h> glvisor.cpp

#include <iostream>
#include <stdio.h>

using std::cout;

#include <lib3ds/material.h>
#include <lib3ds/matrix.h>
#include <lib3ds/mesh.h>
#include <lib3ds/node.h>
#include <lib3ds/vector.h>
#include <qcolordialog.h>

#include "glvisor.h"