3

I am drawing a lot of triangles quite fast using Vertex Array:

void initializeGL() { 
   ...
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, vertices);
   glColorPointer(3,  GL_FLOAT, 0,   colors);
}

void paintGL() {
   ...  
   glDrawElements(GL_TRIANGLES, 3*numTriangles, GL_UNSIGNED_INT, indices);
 }

However I want my rendering to go even faster by using Vertex Buffer Objects (VBO).

Am I right in my understanding that the glVertexPointer() tells the GPU where in the CPU it can fetch the vertices data and then the GPU copy it from this location in the CPU for each paintGL()?

And that using VBO would improve on this by writing the vertices data to the GPU only once?

SInce I am using Qt I try to use the QGLBuffer class:

void GLWidget::initializeGL() {
   ... 
   vertexBuffer = new QGLBuffer(QGLBuffer::VertexBuffer);
   vertexBuffer->create();
   vertexBuffer->bind();
   vertexBuffer->setUsagePattern(QGLBuffer::StaticDraw);
   vertexBuffer->allocate(vertices, 3*numVertices*sizeof(float)); // copies vertices to GPU? 
   vertexBuffer->release();

   #define BUFFER_OFFSET(bytes) ((GLubyte*) NULL + (bytes)) 
   glEnableClientState(GL_VERTEX_ARRAY);
   glEnableClientState(GL_COLOR_ARRAY);
   glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
   glColorPointer(3, GL_FLOAT, 0,  colors);
}  

When I run this it hangs for a long time before crashing :-(. If I comment out the vertexBuffer->release(); line it displays nothing at all but do not crash.

What am I doing wrong here?

Also: how can I likewise send my colors to the GPU only once? There is no QGLBuffer::ColorBuffer type!?

EDIT: I included GLee.[h/c] in my project and replaced the QGLBuffer calls by:

unsigned int vbufferid;
glGenBuffers(1, &vbufferid);  
glBindBuffer(GL_ARRAY_BUFFER, vbufferid); 
int size = 3*numVertices*sizeof(float);
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);

but the code still does not draw anything!?

The vbufferid is assigned the value 1 by the glGenBuffers call so I do not think that the problem is my graphics card.

EDIT 2: I found that if I comment out the glEnableClientState(GL_COLOR_ARRAY); line the triangles are being displayed with VBO (but no color)!

So how do I transfer the color to the GPU when using VBO for vertices?

There is no GL_COLOR_BUFFER type !?

6
  • If think you need to use the glBufferData function when dealing with VBO, the glXYZPointer methods aren't used. But i might be mistaken here. Still i'm pretty sure you need glBufferData. How do your fragment and vertex shader look like? Are you sure the problem isn't there? Commented Dec 21, 2012 at 14:22
  • @dowhilefor: You still must tell OpenGL where in the buffer to find the vertex data, and which size each element they have. So yes, you still need to use the gl…Pointer functions. Commented Dec 21, 2012 at 14:27
  • @dowhilefor: Thanks for helping out, but I think you are wrong. If I comment out the glVertexPointer call the program crash. Also my code is very similar to the example on page 104 of The Open GL Programming guide (which also uses glVertexPointer). Commented Dec 21, 2012 at 14:29
  • Oh ok than don't mind me :) its along time since i used opengl. Commented Dec 21, 2012 at 14:34
  • See my answer. Colors are just another vertex attribute. They go into GL_ARRAY_BUFFER as well. Commented Dec 21, 2012 at 14:38

1 Answer 1

1

Am I right in my understanding that the glVertexPointer() tells the GPU where in the CPU it can fetch the vertices data and then the GPU copy it from this location in the CPU for each paintGL()?

And that using VBO would improve on this by writing the vertices data to the GPU only once?

That's about the general idea, though the actual execution (you don't have to care about) is a bit tricker.

One common misconception you fell for: OpenGL is not "initialized". You can upload certain resources to OpenGL in a initial phase, of course, but as a state machine OpenGL needs to be put into the required state right before you need it.

And then there's your real problem: Your vertex positions are in a buffer object. Your colors however are not. But you're giving OpenGL a pointer, which it misinterprets as a offset into the vertex buffer.

So I suggest you move the following lines right before the glDrawElements call:

#define BUFFER_OFFSET(bytes) ((GLubyte*) NULL + (bytes)) 
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER, vbufferid);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));

glBindBuffer(GL_ARRAY_BUFFER, 0); // or put the colors into a VBO as well
glColorPointer(3, GL_FLOAT, 0,  colors);

glDrawElements(…

And take note, that the buffer is explicitly bound before doing the calls to glVertexPointer and unbound before the color pointer.

But, having some of the attributes in a VBO, and some not defeats the purpose. I hence suggest you move all your vertex attributes into VBOs, preferrably a single VBO, either one array after another (stride 0 and with offset between them) or interleaved (stride= the size of a whole attribute vector, with offset being the offset into the first attribute vector).

Update Code example:

Uploading to VBO

unsigned int vbufferid;
glGenBuffers(1, &vbufferid);  
glBindBuffer(GL_ARRAY_BUFFER, vbufferid); 

int vertexbufsize = (3)*numVertices*sizeof(float);
int colorbufsize = (3)*numVertices*sizeof(float);
glBufferData(GL_ARRAY_BUFFER, vertexbufsize + colorbufsize, NULL, GL_STATIC_DRAW); // data=NULL : initialize, but don't copy

glBufferSubData(GL_ARRAY_BUFFER, 0, vertexbufsize, vertices);
glBufferSubData(GL_ARRAY_BUFFER, vertexbufsize, colorbufsize, vertices);

Drawing with combined VBO

#define BUFFER_OFFSET(bytes) ((GLubyte*) NULL + (bytes)) 
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glBindBuffer(GL_ARRAY_BUFFER, vbufferid);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
glColorPointer(3, GL_FLOAT, 0,  BUFFER_OFFSET(vertexbufsize));

glDrawElements(…
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your help datenwolf! That worked :-). I do not have to move all the enable/bind/xyzPointer code into my paintGL() though. For me it works even if I only do this once in my initializeGL(). But the cost of running these lines for each repaint is maybe not so large? They are only telling OpenGL where to look for data, not copying any data from CPU to GPU?
my 8M triangles are now rotating at a nice FPS :-). What would you suggest the next optimization could be? Maybe using a VOB for the indices? Maybe calculating the colors in the vertex shader?
@Andy: Yes a Index array would be the next step. Buffer type GL_INDEX_BUFFER. Then I'd also suggest to interleave vertex position and color attributes. Right now the layout is PPP…PCCC…C. You get a better memory locality by interlaving them, i.e. PCPCPC…PC.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.