The Quad with DrawArrays

Introduction
With the release of OpenGL 3.0 a “deprecated” model has been introduced, functions that would be removed in future versions of OpenGL were flagged as deprecated so programmers could avoid using them and start pulling them out of current implementations. Many of the “helper” functions were removed such as matrix/stack operations and default lighting. But more important for defining primitives you can no longer use “glVertex”. OpenGL instead forces us to use VAO’s (Vertex Array Object) and VBO’s (Vertex Buffer Object). We’ll use these to draw a quad on the screen.

“Vertex Array Object” and “Vertex Buffer Object”
A way to speed up rendering algorithms is to put the data on the GPU instead of sending them from the CPU to the GPU every time. This is what the VAO’s and VBO’s accomplish, you use a certain amount of memory on the graphics card itself.

Think about a VAO as a complete object definition, and it is made out of different VBO’s. Each VBO can hold specific data (mixing or interleaving is also possible), you could have a VBO for: vertex position, color, normal, texture coordinates, …. VAO’s can hold these different VBO’s in what is called an “attribute list”. By default 16 of these attribute lists can be used in one VAO from 0 to 15.

Setting up a VAO, or any other object for that matter, follows the following rules:
 * Request a memory location (returns integer number, the ID, of the object)
 * Bind the object using the ID
 * Manipulate the object (the object is whichever object OpenGL is currently bound to)
 * Unbind the object using the ID

In code (VAO):  int vaoId = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoId); // Do something with it GL30.glBindVertexArray(0);

In code (VBO):  int vboId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); // Do something with it GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

Setting up the quad VBO and VAO
When putting a VBO in an attribute list of a certain VAO you should first bind the VAO and then call “glVertexAttributePointer” which needs the ID of the list (0 to 15 by default). In our example we are going to setup a quad using one VAO and one VBO. The VBO will hold the vertex positional data and the VAO will function as the main object (the quad) definition.

First we have to declare our vertices, OpenGL draws everything in triangles. A triangle is drawn by defining the vertices in counter clockwise order by default. This means that if you specify the vertices in counter clockwise order and you are defining which side is the front and which is the back. This is used so OpenGL can “cull” invisible faces in an optimization stage of the rendering pipeline. You could change this default behavior but it’s rather trivial so I will not go into depth now.

Going back to our vertex definition, a quad is not a triangle but it can be made from 2 triangles. So to define our quad we will need 6 vertices instead of 4 (we’ll solve this inefficiency another time):



Our default OpenGL coordinate system goes from -1 to 1 in all axis. And this is mapped to the entire screen. Like this:



We’ll define our vertices using 3 numbers, one for each axis (X, Y, Z). Behind the scenes OpenGL works with 4 numbers (X, Y, Z, W). We are not concerned with the W value, just make sure it’s 1 when declaring it. The numbers are floats and we’ll have to wrap them in a bytebuffer (of the type FloatBuffer) before using it in OpenGL methods.

 // OpenGL expects vertices to be defined counter clockwise by default float[] vertices = { // Left bottom triangle -0.5f, 0.5f, 0f, -0.5f, -0.5f, 0f, 0.5f, -0.5f, 0f, // Right top triangle 0.5f, -0.5f, 0f, 0.5f, 0.5f, 0f, -0.5f, 0.5f, 0f }; // Sending data to OpenGL requires the usage of (flipped) byte buffers FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length); verticesBuffer.put(vertices); verticesBuffer.flip;

vertexCount = 6;

We’ll keep track of the vertex count, as we’ll need to let OpenGL know how many vertices it needs to use when drawing the triangles later using the “glDrawArrays” method.

Now that we have our vertices in a usable format we can define our VAO and VBO. In the VBO we’ll put our positional data, in our VAO we’ll put our VBO in the attribute list 0. Linking a VBO with a VAO’s attribute list is fairly easy (when not interleaving data). All we need to do is use “glVertexAttribPointer” as parameters it needs to know the following:
 * Index of the attribute list (0 in our case)
 * How many values define one data definition (3 floats is 1 position definition in our case)
 * What type the values have (floats in our case)
 * The stride and the offset (we are not using those yet, they have to do with interleaving data)

To put the data in the VBO we’ll use the “glBufferData” method. It has also a number of parameters:
 * The type (we’re using a GL_ARRAY_BUFFER, a default definition for generic data)
 * The buffer (our FloatBuffer that holds the vertices positions)
 * The usage (our vertices will not move or change so the usage is simply GL_STATIC_DRAW)

We must not forget to “unbind” our objects once we no longer need to manipulate them. If we combine all the previous steps in code it looks like this:

 // Create a new Vertex Array Object in memory and select it (bind) // A VAO can have up to 16 attributes (VBO's) assigned to it by default vaoId = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoId);

// Create a new Vertex Buffer Object in memory and select it (bind) // A VBO is a collection of Vectors which in this case resemble the location of each vertex. vboId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW); // Put the VBO in the attributes list at index 0 GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0); // Deselect (bind to 0) the VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

// Deselect (bind to 0) the VAO GL30.glBindVertexArray(0);

vaoId and vboId (and also vertexCount) are integers variables defined globally.

Rendering with glDrawArrays
To actually draw our quad we’ll use, as mentioned before, the “glDrawArrays” method. It needs to know the following:
 * How to draw these vertices (we’re using simple GL_TRIANGLES)
 * The first index (we’ll start from the beginning at 0)
 * The vertex count (we’ve held that number in our vertexCount variable)

OpenGL must also have the VAO (and as a result the linked VBO’s) active in memory so we’ll have to bind them before drawing. Our VBO is linked with the attribute list 0 of our VAO, so we’ll have to enable that list as well. When we’ve drawn our quad we’ll unbind and disable everything again. The render code is this:

 GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

// Bind to the VAO that has all the information about the quad vertices GL30.glBindVertexArray(vaoId); GL20.glEnableVertexAttribArray(0);

// Draw the vertices GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertexCount);

// Put everything back to default (deselect) GL20.glDisableVertexAttribArray(0); GL30.glBindVertexArray(0);

Cleaning up our memory
And finally before exiting our application we’ll do some memory management:

 // Disable the VBO index from the VAO attributes list GL20.glDisableVertexAttribArray(0);

// Delete the VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboId);

// Delete the VAO GL30.glBindVertexArray(0); GL30.glDeleteVertexArrays(vaoId);

The result
And there we have it, our quad:



Complete source code
 import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.ContextAttribs; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.PixelFormat; import org.lwjgl.util.glu.GLU;

public class TheQuadExampleDrawArrays { // Entry point for the application public static void main(String[] args) { new TheQuadExampleDrawArrays; }	// Setup variables private final String WINDOW_TITLE = "The Quad: glDrawArrays"; private final int WIDTH = 320; private final int HEIGHT = 240; // Quad variables private int vaoId = 0; private int vboId = 0; private int vertexCount = 0; public TheQuadExampleDrawArrays { // Initialize OpenGL (Display) this.setupOpenGL; this.setupQuad; while (!Display.isCloseRequested) { // Do a single loop (logic/render) this.loopCycle; // Force a maximum FPS of about 60 Display.sync(60); // Let the CPU synchronize with the GPU if GPU is tagging behind Display.update; }		// Destroy OpenGL (Display) this.destroyOpenGL; }	public void setupOpenGL { // Setup an OpenGL context with API version 3.2 try { PixelFormat pixelFormat = new PixelFormat; ContextAttribs contextAtrributes = new ContextAttribs(3, 2) .withForwardCompatible(true) .withProfileCore(true); Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT)); Display.setTitle(WINDOW_TITLE); Display.create(pixelFormat, contextAtrributes); GL11.glViewport(0, 0, WIDTH, HEIGHT); } catch (LWJGLException e) { e.printStackTrace; System.exit(-1); }		// Setup an XNA like background color GL11.glClearColor(0.4f, 0.6f, 0.9f, 0f); // Map the internal OpenGL coordinate system to the entire screen GL11.glViewport(0, 0, WIDTH, HEIGHT); this.exitOnGLError("Error in setupOpenGL"); }	public void setupQuad { // OpenGL expects vertices to be defined counter clockwise by default float[] vertices = { // Left bottom triangle -0.5f, 0.5f, 0f, -0.5f, -0.5f, 0f, 0.5f, -0.5f, 0f, // Right top triangle 0.5f, -0.5f, 0f, 0.5f, 0.5f, 0f, -0.5f, 0.5f, 0f };		// Sending data to OpenGL requires the usage of (flipped) byte buffers FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length); verticesBuffer.put(vertices); verticesBuffer.flip; vertexCount = 6; // Create a new Vertex Array Object in memory and select it (bind) // A VAO can have up to 16 attributes (VBO's) assigned to it by default vaoId = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoId); // Create a new Vertex Buffer Object in memory and select it (bind) // A VBO is a collection of Vectors which in this case resemble the location of each vertex. vboId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW); // Put the VBO in the attributes list at index 0 GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0); // Deselect (bind to 0) the VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); // Deselect (bind to 0) the VAO GL30.glBindVertexArray(0); this.exitOnGLError("Error in setupQuad"); }	public void loopCycle { GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); // Bind to the VAO that has all the information about the quad vertices GL30.glBindVertexArray(vaoId); GL20.glEnableVertexAttribArray(0); // Draw the vertices GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, vertexCount); // Put everything back to default (deselect) GL20.glDisableVertexAttribArray(0); GL30.glBindVertexArray(0); this.exitOnGLError("Error in loopCycle"); }	public void destroyOpenGL { // Disable the VBO index from the VAO attributes list GL20.glDisableVertexAttribArray(0); // Delete the VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboId); // Delete the VAO GL30.glBindVertexArray(0); GL30.glDeleteVertexArrays(vaoId); Display.destroy; }	public void exitOnGLError(String errorMessage) { int errorValue = GL11.glGetError; if (errorValue != GL11.GL_NO_ERROR) { String errorString = GLU.gluErrorString(errorValue); System.err.println("ERROR - " + errorMessage + ": " + errorString); if (Display.isCreated) Display.destroy; System.exit(-1); }	} }

Credit
Mathias Verboven (moci)