Java-Gaming.org Java4K winners: [ by our judges | by the community ]         
Featured games (67)
games approved by the League of Dukes
Games in Showcase (∞)
games submitted by our members



News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  Print  
  Introduction to Vertex Arrays and Vertex Buffer Objects (OpenGL)  (Read 5584 times)
0 Members and 1 Guest are viewing this topic.
Online Riven
« League of Dukes »

JGO Kernel
*****

Posts: 5869
Medals: 255


Hand over your head.


« on: 2011-05-23 15:01:59 »

Introduction to Vertex Arrays and Vertex Buffer Objects
Copy 'n Paste OpenGL



It is my suspicion that most aspiring developers these days prefer to copy snippets of code, paste them into their own project, see if it works, modify a few things, and work from there. Therefore I decided not to explain too much in this tutorial, because most is either self descriptive or properly explained elsewhere (see here).

Having said that, here goes:

Getting started...
First we initialize an OpenGL context through LWJGL:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
import org.lwjgl.*;
import org.lwjgl.opengl.*;
import static org.lwjgl.opengl.ARBBufferObject.*;
import static org.lwjgl.opengl.ARBVertexBufferObject.*;
import static org.lwjgl.opengl.GL11.*;

public class JgoVbo
{
   static void initContext() throws LWJGLException
   {
      int w = 640;
      int h = 480;

      Display.setDisplayMode(new DisplayMode(w, h));
      Display.setFullscreen(false);
      Display.create();
      glViewport(0, 0, w, h);
   }


Render loop
Once we have the context, we need some loop that will prepare a frame, render some graphics and showing it on screen, until we close the window.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
   static void renderLoop()
   {
      while (!Display.isCloseRequested())
      {
         preRender();

         render();

         Display.update();

         Display.sync(10 /* desired fps */);
      }

      Display.destroy();
   }


Clearing the screen
Before the render anything, we need to start with a clean slate by clearing the framebuffer and resetting the the OpenGL matrices.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
   static void preRender()
   {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

      glMatrixMode(GL_PROJECTION);
      glLoadIdentity();

      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();
   }


Immediate mode
Now lets start simple, by drawing a triangle using the immediate mode of OpenGL:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
   static void drawImmediateMode()
   {
      glBegin(GL_TRIANGLES);

      glColor3f(1, 0, 0);
      glVertex3f(-0.5f, -0.5f, 0.0f);

      glColor3f(0, 1, 0);
      glVertex3f(+0.5f, -0.5f, 0.0f);

      glColor3f(0, 0, 1);
      glVertex3f(+0.5f, +0.5f, 0.0f);

      glEnd();
   }


Output:

Vertex Arrays
We can draw exactly the same triangle using Vertex Arrays, like this:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
   static void drawVertexArray()
   {
      // create geometry buffers
     FloatBuffer cBuffer = BufferUtils.createFloatBuffer(9);
      cBuffer.put(1).put(0).put(0);
      cBuffer.put(0).put(1).put(0);
      cBuffer.put(0).put(0).put(1);
      cBuffer.flip();

      FloatBuffer vBuffer = BufferUtils.createFloatBuffer(9);
      vBuffer.put(-0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(+0.5f).put(0.0f);
      vBuffer.flip();

      //

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glColorPointer(3, /* stride */3 << 2, cBuffer);
      glVertexPointer(3, /* stride */3 << 2, vBuffer);
      glDrawArrays(GL_TRIANGLES, 0, /* elements */3);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);
   }


Vertex Buffer Objects
With Vertex Buffer Objects (VBOs) we need to first generate resources on the GPU, and access them by their handles. The idea is to keep that geometry as long as possible on the GPU, so, as opposed to Vertex Arrays, we don't have to upload them every frame.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
   static void drawVertexBufferObject()
   {
      // create geometry buffers
     FloatBuffer cBuffer = BufferUtils.createFloatBuffer(9);
      cBuffer.put(1).put(0).put(0);
      cBuffer.put(0).put(1).put(0);
      cBuffer.put(0).put(0).put(1);
      cBuffer.flip();

      FloatBuffer vBuffer = BufferUtils.createFloatBuffer(9);
      vBuffer.put(-0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(+0.5f).put(0.0f);
      vBuffer.flip();

      //

      IntBuffer ib = BufferUtils.createIntBuffer(2);

      glGenBuffersARB(ib);
      int vHandle = ib.get(0);
      int cHandle = ib.get(1);

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, vHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, vBuffer, GL_STATIC_DRAW_ARB);
      glVertexPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, cHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, cBuffer, GL_STATIC_DRAW_ARB);
      glColorPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

      glDrawArrays(GL_TRIANGLES, 0, 3 /* elements */);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);

      // cleanup VBO handles
     ib.put(0, vHandle);
      ib.put(1, cHandle);
      glDeleteBuffersARB(ib);
   }


Vertex Buffer Objects (indexed)
Now we might want to render the trangle using indexed geometry. That means you have a buffer that holds the indices of the triangles we want to render. This way you can reuse geometry (vertices) for more than one triangle (shared vertices). The only difference is that we need to create an index buffer, which also is a VBO, and fill it. In most cases 16 bit indices are a good choice:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
   static void drawVertexBufferObjectIndexed()
   {
      // create geometry buffers
     FloatBuffer cBuffer = BufferUtils.createFloatBuffer(9);
      cBuffer.put(1).put(0).put(0);
      cBuffer.put(0).put(1).put(0);
      cBuffer.put(0).put(0).put(1);
      cBuffer.flip();

      FloatBuffer vBuffer = BufferUtils.createFloatBuffer(9);
      vBuffer.put(-0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(-0.5f).put(0.0f);
      vBuffer.put(+0.5f).put(+0.5f).put(0.0f);
      vBuffer.flip();

      // create index buffer
     ShortBuffer iBuffer = BufferUtils.createShortBuffer(3);
      iBuffer.put((short) 0);
      iBuffer.put((short) 1);
      iBuffer.put((short) 2);
      iBuffer.flip();

      //

      IntBuffer ib = BufferUtils.createIntBuffer(3);

      glGenBuffersARB(ib);
      int vHandle = ib.get(0);
      int cHandle = ib.get(1);
      int iHandle = ib.get(2);

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, vHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, vBuffer, GL_STATIC_DRAW_ARB);
      glVertexPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, cHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, cBuffer, GL_STATIC_DRAW_ARB);
      glColorPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

      glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, iHandle);
      glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, iBuffer, GL_STATIC_DRAW_ARB);

      glDrawElements(GL_TRIANGLES, /* elements */3, GL_UNSIGNED_SHORT, 0L);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
      glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);

      // cleanup VBO handles
     ib.put(0, vHandle);
      ib.put(1, cHandle);
      ib.put(2, cHandle);
      glDeleteBuffersARB(ib);
   }


Vertex Buffer Objects (interleaved)
Until now we have created separate VBOs for vertices and colors. We can interleave them in two ways:

1. VCVCVC (V stands for vertex element, C stands for color element)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
   static void drawVertexBufferObjectInterleaved1()
   {
      // create geometry buffer (both vertex and color)
     FloatBuffer vcBuffer = BufferUtils.createFloatBuffer(9 + 9);

      vcBuffer.put(-0.5f).put(-0.5f).put(0.0f); // v
     vcBuffer.put(1).put(0).put(0); // c

      vcBuffer.put(+0.5f).put(-0.5f).put(0.0f); // v
     vcBuffer.put(0).put(1).put(0); // c

      vcBuffer.put(+0.5f).put(+0.5f).put(0.0f); // v
     vcBuffer.put(0).put(0).put(1); // c

      vcBuffer.flip();

      //

      IntBuffer ib = BufferUtils.createIntBuffer(1);

      glGenBuffersARB(ib);
      int vcHandle = ib.get(0);

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, vcHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, vcBuffer, GL_STATIC_DRAW_ARB);
      glVertexPointer(3, GL_FLOAT, /* stride **/(3 * 2) << 2, /* offset **/0 << 2); // float at index 0
     glColorPointer(3, GL_FLOAT, /* stride **/(3 * 2) << 2, /* offset **/(3*1) << 2); // float at index 3

      glDrawArrays(GL_TRIANGLES, 0, 3 /* elements */);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);

      // cleanup VBO handles
     ib.put(0, vcHandle);
      glDeleteBuffersARB(ib);
   }


2. VVVCCC (V stands for vertex element, C stands for color element)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
   static void drawVertexBufferObjectInterleaved2()
   {
      // create geometry buffer (both vertex and color)
     FloatBuffer vcBuffer = BufferUtils.createFloatBuffer(9 + 9);

      vcBuffer.put(-0.5f).put(-0.5f).put(0.0f); // v
     vcBuffer.put(+0.5f).put(-0.5f).put(0.0f); // v
     vcBuffer.put(+0.5f).put(+0.5f).put(0.0f); // v
     vcBuffer.put(1).put(0).put(0); // c      
     vcBuffer.put(0).put(1).put(0); // c      
     vcBuffer.put(0).put(0).put(1); // c

      vcBuffer.flip();

      //

      IntBuffer ib = BufferUtils.createIntBuffer(1);

      glGenBuffersARB(ib);
      int vcHandle = ib.get(0);

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, vcHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, vcBuffer, GL_STATIC_DRAW_ARB);
      glVertexPointer(3, GL_FLOAT, /* stride **/(3 * 1) << 2, /* offset */0 << 2); // float at index 0
     glColorPointer(3, GL_FLOAT, /* stride **/(3 * 1) << 2, /* offset */(3 * 3) << 2); // float at index 9

      glDrawArrays(GL_TRIANGLES, 0, 3 /* elements */);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);

      // cleanup VBO handles
     ib.put(0, vcHandle);
      glDeleteBuffersARB(ib);
   }

Pay close attention to the values specified as stride and offset in both examples of interleaved rendering.


Mapped Vertex Buffer Object
We can also request a memory region from the driver, to push our vertex data into. We obtain this memory region as a ByteBuffer, in which we can solely write, using the glMapBuffer(..). When we've defined the data, we call glUnmapBuffer(...) to notify the driver we are done, allowing the driver exclusive access to the data.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
  static void drawVertexBufferObjectInterleavedMapped()
   {
      IntBuffer ib = BufferUtils.createIntBuffer(1);

      glGenBuffersARB(ib);
      int vcHandle = ib.get(0);

      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_COLOR_ARRAY);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, vcHandle);
      glBufferDataARB(GL_ARRAY_BUFFER_ARB, (9 + 9) << 2, GL_STATIC_DRAW_ARB);

      {
         ByteBuffer dataBuffer = glMapBufferARB(GL_ARRAY_BUFFER_ARB, ARBBufferObject.GL_WRITE_ONLY_ARB, (9 + 9) << 2, null);

         // create geometry buffer (both vertex and color)
        FloatBuffer vcBuffer = dataBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();

         vcBuffer.put(-0.5f).put(-0.5f).put(0.0f); // v
        vcBuffer.put(1).put(0).put(0); // c

         vcBuffer.put(+0.5f).put(-0.5f).put(0.0f); // v
        vcBuffer.put(0).put(1).put(0); // c

         vcBuffer.put(+0.5f).put(+0.5f).put(0.0f); // v
        vcBuffer.put(0).put(0).put(1); // c

         vcBuffer.flip();        

         glUnmapBufferARB(GL_ARRAY_BUFFER_ARB);
      }

      glVertexPointer(3, GL_FLOAT, /* stride */(3 * 2) << 2, /* offset */0L << 2); // float at index 0
     glColorPointer(3, GL_FLOAT, /* stride */(3 * 2) << 2, /* offset */(3 * 1) << 2); // float at index 3

      glDrawArrays(GL_TRIANGLES, 0, 3 /* elements */);

      glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

      glDisableClientState(GL_COLOR_ARRAY);
      glDisableClientState(GL_VERTEX_ARRAY);


      // cleanup VBO handles
     ib.put(0, vcHandle);
      glDeleteBuffersARB(ib);
   }


Done!
Finally we can render our triangle, using the 7 strategies mentioned above:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
   static void render()
   {
      glClearColor(1, 1, 1, 1);

      // start drawing a triangle with the different strategies

      drawImmediateMode();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexArray();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexBufferObject();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexBufferObjectIndexed();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexBufferObjectInterleaved1();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexBufferObjectInterleaved2();

      glTranslatef(+0.05f, -0.05f, 0);

      drawVertexBufferObjectMapped();
   }

To prevent the triangles to be rendered on top of eachother, a slight offset/translation is added after drawing each triangle.

Output:

Although I agree the output is far from entertaining, it should help you implementing your own geometry rendering strategy.

Hi, appreciate more people! Σ ♥ = ¾

Learn how to award medals... and work your way up the social rankings
Offline CommanderKeith

JGO Wizard
****

Posts: 1455
Medals: 9



« Reply #1 on: 2011-05-23 15:34:08 »

Great stuff, you could give courses with these notes. Succinct and symetrical in the way the strategies are laid out so only the differences can be seen.
Thanks for sharing

Offline static_flashlight

JGO n00b
*

Posts: 22


Software Engineer


« Reply #2 on: 2011-07-08 07:54:20 »

Thanks for posting this tutorial.  Unlike far too many examples online for LWJGL, this tutorial includes all the syntax required to actually use these features.
Games published by our own members! Go get 'em!
Online Riven
« League of Dukes »

JGO Kernel
*****

Posts: 5869
Medals: 255


Hand over your head.


« Reply #3 on: 2011-07-08 08:59:35 »

Ofcourse, I needed it to work to create the screenshots Smiley

Hi, appreciate more people! Σ ♥ = ¾

Learn how to award medals... and work your way up the social rankings
Offline loom_weaver

Full Member
**

Posts: 224
Medals: 14



« Reply #4 on: 2011-07-10 00:15:47 »

Although it's easy to figure out, you're missing main():

1  
2  
3  
4  
5  
6  
    public static void main(String[] args) throws LWJGLException {
        System.out.println("Hello world from OpenGL!");

        initContext();
        renderLoop();
    }
Online Riven
« League of Dukes »

JGO Kernel
*****

Posts: 5869
Medals: 255


Hand over your head.


« Reply #5 on: 2011-10-18 15:36:01 »

Thanks. Added glMapBuffer(..) variant.

Hi, appreciate more people! Σ ♥ = ¾

Learn how to award medals... and work your way up the social rankings
Pages: [1]
  Print  
 
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.16 | SMF © 2011, Simple Machines Valid XHTML 1.0! Valid CSS!
Page created in 0.102 seconds with 20 queries.