![]() |
|
OpenGL and Immediate Mode |
decepto
Member #7,102
April 2006
![]() |
Hi guys, As some of you may know, I'm working on a game. This is my first serious foray into 3D game development, and I'm having a bit of an issue with OpenGL. Thus far, I've always used immediate mode for rendering objects in opengl. This was fine when my polygon count was low. However, my game requires that thousands of polys be rendered, so immediate mode is out. So far I've come across several non-immediate-mode methods for rendering polygons. They include: vertex arrays, display lists, vertex shaders, vertex buffer objects, and vertex array objects. I understand (mostly) what display lists are, but I'm not certain that vertex arrays, vertex buffer objects, and vertex array objects are actually different methods. I'm assuming that there is a bit of overlap. Anyways, I'd like to be using the most recent and "standard" method as possible. However, I'd also like to be able to support older graphics cards that only allow for OpenGL 2.1. Which method should I be using to render polygons? Thanks for the help. edit: just ranting. Why the hell does current opengl literature even mention glBegin()/glEnd(). These calls are depreciated, and only do a disservice to people trying to learn the api! -------------------------------------------------- |
X-G
Member #856
December 2000
![]() |
decepto said: vertex arrays, vertex buffer objects, and vertex array objects They're all slightly different (from different vendors/times) ways of doing the same thing. Vertex buffer objects (VBOs) are the standardized (non-extension) method and what you ought to be doing. EDIT: VBOs have been standard since OpenGL 1.4 something, so you should have no problem using them. Quote: These calls are depreciated They were only deprecated relatively recently in OpenGL 3.0, unless you're talking about OpenGL ES where they don't exist at all. And they're much easier to understand than the full complexity of VBOs, so they serve a purpose as a gentle introduction for the beginner who doesn't have the hang of things yet. -- |
Thomas Fjellstrom
Member #476
June 2000
![]() |
I recall reading about some tests one guy did in seeing which method was the "best", and in his tests, it seemed to show that using VBOs with vertex arrays seemed to provide a little extra boost over just one or the other. I'm not entirely sure how thats supposed to work though. I think the data was stored in the VBO, but glVertexPointer and/or glDrawArrays was still used to actually tell gl how to use it. edit: Never mind, after some searching, it seems thats just "how its done". -- |
Archon
Member #4,195
January 2004
![]() |
decepto said: They include: vertex arrays, display lists, vertex shaders, vertex buffer objects, and vertex array objects. I understand (mostly) what display lists are, but I'm not certain that vertex arrays, vertex buffer objects, and vertex array objects are actually different methods. I'm assuming that there is a bit of overlap. VBOs are what you want if you want to go beyond glBegin/glEnd and the associated glVertex/glTexCoord/glNormal commands. I think that Vertex Arrays is a simpler version of VBOs. Display Lists are just precompiled OpenGL commands that you index and call later. You can use this for rendering fonts as you can glCallLists(array_of_characters). Vertex Shaders is a counterpart to Fragment/Pixel Shaders. They let you program what happens to the vertices that you send into the rendering pipeline. |
OICW
Member #4,069
November 2003
![]() |
As X-G already said, immediate mode doesn't bother you with such things like: "how the hell am I gonna pull this the array in correct order and then push it to the GPU?" that are prevalent, when you decide to use VBO's. In the Redbook they say, that VBO's can be faster, because you don't call certain methods many times. That and the fact, that if done properly, once your data are pushed into the GPU they pretty much stay there unless you need to change them quite often. For more info refer to the chapter 2, section "Vertex buffers" in the Redbook. [My website][CppReference][Pixelate][Allegators worldwide][Who's online] |
Bob
Free Market Evangelist
September 2000
![]() |
Let me do a run-down of the methods: 1. Immediate mode. Pretty self-explanatory. Do a bunch of API calls, each setting some particular aspect of the vertex, then sending the vertex position to trigger rendering. Repeat for all vertices. There is a pretty big overhead in calling glBegin() (the driver needs to validate a 2. Vertex arrays. Doing an API call per attribute, times the number of vertices is a lot of CPU work! Instead, fill in some arrays with all your values, and point the driver to it. The driver can then easily memcpy() the data over to the GPU for each rendered vertex. This eliminates the call overhead. However, the driver has no way of knowing that the arrays are "static". That is, the driver doesn't know if you changed the arrays or not, so it needs to always read them to copy the data over to the GPU. So in that aspect, vertex arrays work just like immediate mode. Avoid having too many glDraw*() calls, for each of those implies a glBegin(), and glBegin() are relatively expensive. 3. VBOs (ARRAY_BUFFER). You wrap your vertex arrays into a buffer that is managed by the driver. You need to explicitly tell the driver whenever this buffer is modified. This allows the driver to avoid having to re-read it every draw call, and thus allows the driver to copy the whole array over to the GPU's fast memory. With the array in GPU memory, vertices can be read at full framebuffer bandwidth (> 100 GB/sec on high-end GPUs). Without this, you're limited to PCI-e 16x, which is ~2.5 GB/sec. Moreover, geometry data is usually unchanged between frames, which allows the driver to avoid having to re-upload the same data over and over again. Otherwise, same issues as with vertex arrays. 4. VBOs (ELEMENT_ARRAY_BUFFER). Not only would you put your vertex attributes in a driver-managed buffer, but also the indices forming the vertices. This may or may not improve performance due to the overhead of managing indices. Performance improvements occur on a case-by-case basis. 5. Display lists. Display lists are built up by the driver into a pre-made command stream to the GPU. Theoretically, display lists should be the fastest thing ever, since they are static and the driver itself can optimize it. Practically, some drivers do only minimal optimizations. You can worry less about too many glBegin() calls inside the display list because the driver can see which state has changed or not changed and thus optimize out the validation. Display lists are accelerated on NVIDIA Quadro GPUs, and are the fastest method of drawing on those cards. Hope this helps. -- |
decepto
Member #7,102
April 2006
![]() |
Thanks for all of the help everyone. -------------------------------------------------- |
Thomas Fjellstrom
Member #476
June 2000
![]() |
Bob said: Display lists are accelerated on NVIDIA Quadro GPUs, and are the fastest method of drawing on those cards. Somewhat surprising. I suppose some older CAD packages use Display lists? -- |
decepto
Member #7,102
April 2006
![]() |
Do you guys know how I would go about rendering multiple VBOs? Here is my setup code: 1 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[0]);
2 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * 3 * 4 * face_group_0.size(), face_group_0_vertices, GL_STATIC_DRAW_ARB);
3 glVertexPointer(3, GL_FLOAT, 0, 0);
4
5 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[0]);
6 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * 2 * 4 * face_group_0.size(), face_group_0_texture, GL_STATIC_DRAW_ARB);
7 glTexCoordPointer(2, GL_FLOAT, 0, 0);
8
9
10 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[1]);
11 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * 3 * 4 * face_group_1.size(), face_group_1_vertices, GL_STATIC_DRAW_ARB);
12 glVertexPointer(3, GL_FLOAT, 0, 0);
13
14 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[1]);
15 glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(GLfloat) * 2 * 4 * face_group_1.size(), face_group_1_texture, GL_STATIC_DRAW_ARB);
16 glTexCoordPointer(2, GL_FLOAT, 0, 0);
And my drawing code: 1 // Render first VBO
2 glEnableClientState(GL_VERTEX_ARRAY);
3 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[0]);
4
5 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
6 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[0]);
7
8 glEnable(GL_TEXTURE_2D);
9 glDrawArrays(GL_QUADS, 0, number_of_vertices);
10 glDisable(GL_TEXTURE_2D);
11
12 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
13 glDisableClientState(GL_VERTEX_ARRAY);
14
15
16 // Render second VBO
17 glEnableClientState(GL_VERTEX_ARRAY);
18 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[1]);
19
20 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
21 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[1]);
22
23 glEnable(GL_TEXTURE_2D);
24 glDrawArrays(GL_QUADS, 0, number_of_vertices);
25 glDisable(GL_TEXTURE_2D);
26
27 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
28 glDisableClientState(GL_VERTEX_ARRAY);
With this code, only vertexBuffer[1] and texBuffer[1] are rendered. The first two aren't showing up. -------------------------------------------------- |
Bob
Free Market Evangelist
September 2000
![]() |
Thomas Fjellstrom said: I suppose some older CAD packages use Display lists? Newer ones too. decepto said: With this code, only vertexBuffer[1] and texBuffer[1] are rendered. The first two aren't showing up It looks to me like you're rendering the same thing twice. gl*Pointer() set the vertex array state, which includes the VBO bindings. So you need to change gl*Pointer() between your draw calls. Ie, replace your render loop by: 1 // Render first VBO
2 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[0]); // Tell GL the current VBO is this
3 glVertexPointer(3, GL_FLOAT, 0, 0); // Now attach the VBO to the vertex pointer
4
5 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[0]);
6 glTexCoordPointer(2, GL_FLOAT, 0, 0); // Attach the other VBO to the texcoord pointer
7
8 // Enable the two arrays
9 glEnableClientState(GL_VERTEX_ARRAY);
10 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
11
12 // Draw
13 glEnable(GL_TEXTURE_2D);
14 glDrawArrays(GL_QUADS, 0, number_of_vertices);
15 glDisable(GL_TEXTURE_2D);
16
17 glDisableClientState(GL_TEXTURE_COORD_ARRAY); // No need to disable the arrays if you're just going to renable the same ones later
18 glDisableClientState(GL_VERTEX_ARRAY);
19
20
21 glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBuffer[1]);
22 glVertexPointer(3, GL_FLOAT, 0, 0);
23
24 glBindBufferARB(GL_ARRAY_BUFFER_ARB, texBuffer[1]);
25 glTexCoordPointer(2, GL_FLOAT, 0, 0);
26
27
28 // Render second VBO
29 glEnableClientState(GL_VERTEX_ARRAY);
30 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
31
32 glEnable(GL_TEXTURE_2D);
33 glDrawArrays(GL_QUADS, 0, number_of_vertices);
34 glDisable(GL_TEXTURE_2D);
35
36 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
37 glDisableClientState(GL_VERTEX_ARRAY);
(The setup code should stay as is) -- |
decepto
Member #7,102
April 2006
![]() |
Thank you Bob. That helps a lot! -------------------------------------------------- |
|