04: Advanced rendering

NOTE: I am a beginner; anything I say in this tutorial may be wrong. The only thing I can promise you is that it works and I will provide reference links to complex methods. I've also use alot of other people's code for reference, generally either giving them credit or linking to their site. If you find anything wrong with the tutorial PLEASE email me at lordofthedancinmushrooms@gmail.com/tweet me/leave a comment :D

Ok, this is when things start getting a little technical.

This is hard to understand just by reading text, so you may want to look at this modified BasicRenderer class as we're going along: Download source (same as link down the page.)

Basically there are four modes of rendering in OpenGL; we're only going to be using 2 of them, one of them you've already met when you did "Basic block rendering" (Immediate mode).

  1. Immediate mode - Every time you complete a set of vertices a draw call is sent to the graphics card (triangles are drawn immediately every frame) Results in slower fps (more draw calls) easily editable
  2. Vertex Arrays - Vertices are stored to an array and then this array is sent to the graphics card to be drawn so everything is done in one big draw call. Faster and can be dynamically modified (values for different vertices can be edited/removed), but slower than Display lists
  3. Display Lists - Very fast as vertices are stored on the GPU itself rather than sent to it; this prevents the data from being dynamically modified though.
  4. Vertex Buffer Object - Similar to Vertex arrays, but alot faster (theoretically), hard to setup and debug.
If you guessed that we'll be using Vertex buffer objects, you guessed right :D


I've decided to split up creating and rendering VBOs into 3 parts to make it easier to explain; I'll then cover how to implement this in our code.

Initialisation

Before we can actually do anything we first have to enable a few OpenGL modes like so (this normal is done at initialisation and doesn't effect our game loop):

    GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
    GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);

Now that's out the way with we need two integers which will store data for the colour of each vertex and the position of each vertex. Why are they stored as ints? I don't honestly know; but I'd imagine it's because integers take up less memory than an array, essentially it stores the data in a compressed format I believe.
After declaring the integers we'll need to format them into a format OpenGL understands by using glGenBuffers (I think this is what happens anyway, unclear from reference) like so:

VBOColorHandle=GL15.glGenBuffers();
VBOVertexHandle=GL15.glGenBuffers();
I generally declare the integers as a private variable inside the class, then use glGenBuffers when creating the VBOs so I can access them in the render method too.

Setup
At First we'll be using something called a FloatBuffer to store information about our vertices. FloatBuffer is essentially a one dimensional array of floats (numbers) that OpenGL can interpret. As it is one dimensional, if I wanted to store one vertex with a x,y,z position I'd need to have an array length of at least 3.
This is created using this code:
FloatBuffer VertexPositionData = BufferUtils.createFloatBuffer(AmountOfVertices*NumberOfDimensions);
(sorry for the downsize, I feel it's easier to read on one line)
For now lets just create two FloatBuffers, one for storing colors and one for storing the actual positions of the points (note: each point must have a color). You can use only one buffer and then refer to which parts are color data and which parts are position data (known as interleaving), but for now just use two.

We can then enter data into our FloatBuffers like so:
VertexPositionData.put(new float[]{0.0f, 0.0f, 0.0f, 0f, 1f, 0f,1.0f, 1.0f, 0.0f,1.0f, 0f, 0f});
Pretty self-explanatory, basically data into our floatbuffer; .put can be used as many times as you want to add data onto the end of the buffer so to speak, until you rewind or flip it.
Flip/Rewind: As we've just entered the data in by tagging it on to the end we need to reverse the order of the FloatBuffer so when we send it off to wherever it needs to go it can be read from start to end (in the right order).
To do this we simply use .Flip or .Rewind as so:
VertexPositionData.flip();
The last thing we need to do is tell OpenGL which buffer we are writing to and then write in the float data into our int buffer:
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOVertexHandle);
    GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexPositionData,GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,0);
Note: When we use glBindBuffer the second argument is the buffer we want to bind to, so using 0 just means we're not writing to any buffer anymore (unbinding).

Rendering
This part is actually pretty simple, and we'll want to use this in our game loop to render our VBOs.
First of we need to use glPushMatrix(), this creates a new copy of the matrix we were altering before (modelview, where we store all the vertex information) for us to edit.
We then rebind our buffers in order and tell OpenGL that it can find different vertex information in those buffers using Pointers. E.g.
        GL11.glPushMatrix();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOVertexHandle);
GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOColorHandle);
    GL11.glColorPointer(3, GL11.GL_FLOAT, 0, 0L);
We can then Draw all the information stored in the buffers by using:
GL11.glDrawArrays(GL11.GL_QUADS, 0, 4);
Where parameter 1 is the type of shape to draw, 2 is the starting index of data in out buffers, 3 is the amount of points(vertexes) to render. Note: if there is not enough points to make up the shape it will not be drawn; e.g. putting 3 will not draw a Quad or even a triangle(half a quad)(unless you are drawing a triangle).
And then we need to delete our copy of the matrix we created at the start by using:
GL11.glPopMatrix();
This prevents problems like the effects translatef and rotatef being carried on into the next gameloop recursively.

"That's all well and good but how do I set this up with the BasicRenderer class?!"

I decided the explanation would take to long to write out; if you think you're smart enough here's two methods to get you started. Alternatively (and because setting up gluPerspective correctly is a bitch) you can download and study a modified BasicRenderer (now called Game) class I made to test out drawing a cube with VBO using the methods described above.

SOURCE DOWNLOAD: here

Example of creating VBOs
private void CreateVBO() {
VBOColorHandle=GL15.glGenBuffers();
VBOVertexHandle=GL15.glGenBuffers();
FloatBuffer VertexPositionData = BufferUtils.createFloatBuffer(4*3);
VertexPositionData.put(new float[]{0.0f, 0.0f, 0.0f, 0f, 1f, 0f,1.0f, 1.0f, 0.0f,1.0f, 0f, 0f});
VertexPositionData.flip();
FloatBuffer VertexColorData = BufferUtils.createFloatBuffer(4*3);
VertexColorData.put(new float[]{1,1,1,1,1,1,1,1,1,0,1,1});
VertexColorData.flip();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOVertexHandle);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexPositionData, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,0);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOColorHandle);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, VertexColorData, GL15.GL_STATIC_DRAW);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,0);
}

Gameloop rendering
private void DrawVBO() {
GL11.glPushMatrix();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOVertexHandle);
GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER,VBOColorHandle);
     GL11.glColorPointer(3, GL11.GL_FLOAT, 0, 0L);
     GL11.glDrawArrays(GL11.GL_LINE_LOOP, 0, 4);
     GL11.glPopMatrix();
}

Next stop: using this to render chunks
For those who like pictures here's something I did for COMP4 which uses VBO for color, position and lighting. This pic was generated using a 2D integer array:

Image not found


Subpages (1): 4.5: Rendering Chunks
Comments