PDA

View Full Version : Buffer Objects with Vertex-Array Data

Oragon
03-20-2012, 12:12 PM
Hello :)

This is not a tiny post, and I am not encountering technical or coding issues with OpenGL. But I am new to 3D coding and OpenGL and I just would like to be sure I understand well the use of Buffer Objects. I would be grateful for anyone who will take the time to read my post and guide me :)

I am learning OpenGL using to the Red Book (7th edition).
I try to understand Buffer Objects. What I ask is "do I code it the good way ? Are there improvements (always using Buffer Objects of course).
I won't give raw code (at least for gl______() calls), but only the ideas of how i understand the use of BOs.

My C++ code contains a class representing an editable Mesh, which is a collection of vertices and indices :

#include "Vector3.hpp"

class Mesh
{
// ....
ListOf<Vector3> Vertices;
ListOf<Uint> Indices;
// ....
};

Vector3 is just a {X,Y,Z}-float structure.
That way if I wish to represent a box meshed with triangles :

Vertices = {
{-1,-1,-1},
{1,-1,-1},
{1,1,-1},
{-1,1,-1},
{-1,-1,1},
{1,-1,1},
{1,1,1},
{-1,1,1}
};

Indices = {
0,1,2,
0,2,3,
1,5,6,
1,6,2,
4,0,3,
4,3,7,
5,4,7,
5,7,6,
3,2,6,
3,6,7,
4,5,1,
4,1,0
};

My Mesh class also has two methods :

Draw : Asks for rendering my mesh; UpdateBuffers : If no buffer object created yet for this mesh, create them. Then send vertices and indices in the BO

For that purpose, my methods are designed as below :

VERTICES = 0
INDICES = 1
Buffers = Array of 2 GLuint; (will contains the Buffer Object names)

Method "Draw"
{
IF [Buffer Objects] are not "UP TO DATE" THEN
[CALL UpdateBuffers Method]
END IF

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffers[INDICES]);
glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));
glDrawElements(GL_TRIANGLES, SIZE(Indices), GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));

}

Method "UpdateBuffers"
{
glEnableClientState(GL_VERTEX_ARRAY);

IF [Buffer Objects] == NOTHING THEN
// ...
glGenBuffers(2, BUFFERS);
// ...
END IF

[... Create the array of vertices, of type : GLfloat[] from Vertices ...] ---> VerticesArray
[... Create the array of indices, of type : GLfloat[] from Indices ...] ---> IndicesArray

glBindBuffer(GL_ARRAY_BUFFER, Buffers[VERTICES]);
glBufferData(GL_ARRAY_BUFFER, SIZE(VerticesArray), VerticesArray, GL_DYNAMIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffers[INDICES]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, SIZE(IndicesArray), IndicesArray, GL_DYNAMIC_DRAW);

[... Free VerticesArray and IndicesArray ...]

}

I know two performance "TODOs" :
- Do not update a whole array but only update modified parts of my BO (using glMapBufferRange or glSubData I suppose) ;
- Only update the modified BO (either Vertices or Indices, not both).

But before going further, I'd like to know if my coding design is doing the "good way" :).

BionicBytes
03-20-2012, 12:28 PM
I notice from your code that you are using the legacy vertex arrays

glVertexPointer(3, GL_FLOAT, 0, BUFFER_OFFSET(0));

There's nothing wrong with that (especially for pre GL 3.0) but perhaps it's worth making the switch now to generic vertex attributes instead (glVertexAttribute) since these are more generic and allow instancing too. The only down side is that you must be using shaders rather than fixed function.

Also, if your data in the buffer objects won't change (or very infreaquently) then it's worth using GL_STATIC_DRAW and you will usually get better rendering performance as a result.

tanzanite
03-21-2012, 12:50 AM
My Mesh class also has two methods :
... UpdateBuffers : If no buffer object created yet for this mesh, create them. Then send vertices and indices in the BO

This sounds like you use separate buffers for every mesh. It is generally highly advisable to use one buffer for multiple meshes as frequent VBO binding changes can hurt performance quite badly (especially if the individual meshes are tiny/small).

Might want to rethink your implementation.

Just to generate some ideas - I use (simplified):
* BufferObject - holds the buffer (handle from glGenBuffers). type / size etc.
* BufferManager (extends BufferObject) - gives out pieces of the BufferObject it holds as BufferRange objects.
* VertexFormat - describes the vertex format (handle from glGenVertexArrays) using one or multiple BufferObject's as sources.
* Mesh - holds VertexFormat and BufferRange (however many of thous it needs [i almost always use two, one for vertex data and one for indices, that actually come from the same BufferManager]). Also, offsets etc for drawing (so i can use thous directly in glDrawRangeElementsBaseVertex (or whatever is appropriate) without the need to infer all that data from VertexFormat and the BufferRange's).

So, to create a Mesh one needs VertexFormat to get the vertex sizes etc and relevant buffers from which to get the BufferRange's to hold the actual data.

In my case, the primary BufferManager holds the whole world (tens of thousands of meshes at bad times) - which is ever changing (So, the specific BufferManager i use for that is implemented as having fully asynchronous updates by default. Using mark-and-sweep in conjunction of synchronization objects to do so).

Oragon
04-05-2012, 01:13 AM
Ok. Thank you both for having answered it :).