4

People constantly tell me to use at least Vertex Arrays. But i think its not a good idea since i'm using glPushMatrix() with glTranslatef/glRotatef to position an object in the 3d world.

So, should i stop using glPushMatrix() and calculate the rotated/moved vertex positions in the world "manually" and then push their vertex data into a vertex array and then render it all at once?

But, all this will get even more messed up when i use different texture surfaces for all the objects on the screen which are also depth sorted.

So:

  1. I would have to store each object with the texture surface ID as well.
  2. I would sort all the visible objects by their Z position (the game is viewed top-down only, and all objects are flat).
  3. I would go through this sorted array:
  4. Create new buffer and copy only the vertex/texcoord/color/normal into this buffer:
  5. Every time the texture surface ID has changed from previous ID, i will bind to the correct texture ID:
  6. Upload the vertex data i collected.
  7. Free the buffer used for temporary vertex array.
  8. Repeat steps 4-7 until i have gone through all the data i sorted at the first place.
  9. Free my sorted array data, and repeat steps 1-9.

Am i doing it right?

Also, how should i design the data structure for the objects i will sort? For example, is it good to use std::vector to store the vertex data for each object? Or is there better alternative? I was thinking that the std::vector to store all this data would look like:

struct GameObject {
    int TexID;
    float z; // we will sort by this
    vector<VTCNStruct> VertexData; // store each point of the object here (including color/normal/texcoord points).
};

vector<GameObject> GameObjectBuffer; // push all sortable objects here

Also, at step 4: is it possible to use the already existing std::vector in this case? I've had the idea i that i must use raw arrays, like new float[100] for sending the vertex array to my GPU, or could i somehow use my already existing sorted std::vector here somehow (efficiently) without creating new buffer every time the texture ID changes?

Newbie
  • 1,143
  • 5
  • 20
  • 30

2 Answers2

10

Please abandon glBegin/glEnd now, it's deprecated and was removed from OpenGL-4. You should definitely use vertex arrays, even better if you use vertex buffer objects.

Using immediate mode (glBegin/glEnd) the OpenGL driver has to build a vertex array from the function calls. As it's not clear how many vertces will arrive, it will eventually re-allocate the memory multiple times (the times in which GPUs directly executed the calls between glBegin/glEnd are long over).

Using a vertex array will never be less performant than immediate mode. It's okay to put multiple object's geometries into a single vertex array, you can separate them through the vertex index lists. Vertex index lists can be stored in buffer objects, too.

Then between drawing the objects adjust the modelview matrix.

Sorting order should be like the followig:

  1. divide into opaque and translucent objects. Sort the opaque
  2. Sort the opqaues by GL objects (texture, shader, materials, etc.) since switching GL objects is most expensive.
  3. For each distinct GL object group sort near to far and draw in that order
  4. Sort translucents far to near and draw in that order

You should not try fiddling around with buffer up-/downloads. Just upload the vertex data one time and then just adjust the index arrays and the order in which those are submitted. There's no need to worry about available OpenGL memory, there's no limit enforced. If your data doesn't fit into GPU RAM the driver is responsible for swapping it to system memory – that's also why you should sort by OpenGL objects first, because every time you switch OpenGL objects (glBindTexture, glBindBuffer, etc.) the driver may need to swap, so you want to keep those swapping operations to a minimum. The amount of data sent to the GPU in form of index arrays will be much less, than what's sent by immediate mode calls at a minimum:

  • Index array: 16 bit per vertex (16 bit size indices are most performant).

  • Immediate mode call: 4*32 bit per vertex

vallentin
  • 23,478
  • 6
  • 59
  • 81
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • `Index array: 16 bit per vertex` VS. `Immediate mode call: 4*32 bit per vertex` Isnt there a miscalculation, because with vertex arrays you also have to send the vertex data. Lets say i send glVertex3f(1,1,1) it will take 3*32 bit + 32bit for function address? but with vertex arrays, i also have to send x,y,z which takes 3*32 bit + index, which takes 16 bit, total saved bits = 16? Or what did i miss? As far as i know you cant store vertex arrays into GPU, so you have to send the vertex data every frame again and again even if it hasnt changed...? – Newbie Dec 14 '10 at 15:58
  • Using a Vertex Buffer Object vertex arrays are indeed stored in GPU memory. Without using VBO explicitly most modern drivers tend to create them in-situ, because that's how modern GPUs work. – datenwolf Dec 15 '10 at 11:13
  • in-situ? what do you mean, modern GPU's convert all vertex arrays into VBO automatically?? – Newbie Dec 15 '10 at 14:48
  • Not the GPU, the drivers. Modern GPUs process geometry data in large chunks residing in their own memory. So any geometry to be drawn must be transferred to the GPU memory first. Thus immediate mode calls must create a vertex array on the fly. The API won't reflect this however, so using a vertex array w/o using VBOs will not result in an accessible VBO. This happens all behind the scenes of the OpenGL API. – datenwolf Dec 15 '10 at 18:10
2

You should keep one vertex array per object. Don't use glTranslatef/glRotatef, etc. Instead collapse the transforms yourself into one matrix. Use that matrix to depth sort your objects. Then draw your objects from front to back by pushing the objects transform matrix, drawing the vertex array, then popping the transform matrix.

This approach means that you aren't constantly creating and freeing arrays to store the vertex data in.

As to using std::vector, most implementations use a raw C array inside, thus, you can do &myVec[0] to get to that array. However, don't try to persist that pointer since the std::vector could change realloc its array and then your persisted pointer would no longer be valid.

Joshua D. Boyd
  • 4,808
  • 3
  • 29
  • 44
  • "You should keep one vertex array per object" - is this smart at all? since most of my objects are just quads, and doesnt this use more bandwidth now when i dont only send more data to GPU (in means of color/normal arrays), but i also would need to send the vertex array upload commands between every object. – Newbie Dec 13 '10 at 12:05
  • How do i collapse my transforms into one matrix? Also, dont you mean from back to front? So, basically you suggest i will keep the `glPushMatrix()` but instead of `glBegin/glEnd` i would use vertex arrays? IF i wanted to use VBO instead, should i keep the same approach you suggested? – Newbie Dec 13 '10 at 12:08
  • Newbie-I'm not an OpenGL programmer, but I believe Josh means that you should draw the frontmost (e.g. closet to screen) objects first, furthest back last. EDIT--By collapsing transforms into one matrix, I believe you can use some math trickery to get one transform to both move/rotate/etc. in one fell swoop. – ZachS Dec 13 '10 at 20:03