The Quad interleaved

Introduction
We’ve been splitting our vertex data into different VBO’s. We could also “interleave” or “mix” these values together. Leaving us with 1 VBO that has all the vertex data mixed together. If you followed along you might think “we only need to use 1 attribute list”. No, we still need to split the different data parts in different lists, we only use 1 VBO to manage the data. Here’s how.

Splitting VBO's
As an overview we’ll do the same thing again but splitting up our VBO’s. As an example we’ve got 2 FloatBuffers, one for the color and one for the position. A color is defined by 4 components, position by 3:

 float[] positions = new float[] {1, 1, 1}; float[] colors = new float[] {1, 1, 1, 1};

// Put our data in the proper format: byte buffers FloatBuffer positionBuffer = BufferUtils.createFloatBuffer(positions.length); positionBuffer.put(positions); positionBuffer.flip; FloatBuffer colorBuffer = BufferUtils.createFloatBuffer(colors.length); colorBuffer.put(colors); colorBuffer.flip;

// Create a new VAO int vaoID = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoID);

// Create a new VBO for our positions and link it to attribute list 0 int posVboID = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, posVboID); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW); GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

// Create a new VBO for our colors and link it to attribute list 1 int colVboID = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colVboID); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, positionBuffer, GL15.GL_STATIC_DRAW); GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 0, 0); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

GL30.glBindVertexArray(0);

For those that have followed along with the previous tutorials, when I’m using “glDrawElements” what am I missing (if anything)?

Interleaving VBO's
Let’s now combine the color and position VBO’s into 1 VBO. We’ll use the same example as before with our made up positions and colors. A more practical example will be shown later.

 float[] positions = new float[] {1, 1, 1}; float[] colors = new float[] {1, 1, 1, 1};

// Interleave the data in the proper format: byte buffer FloatBuffer interleavedBuffer = BufferUtils.createFloatBuffer(positions.length + 		colors.length); interleavedBuffer.put(positions); 	// Buffer contents: X, Y, Z interleavedBuffer.put(colors);		// Buffer contents: X, Y, Z, R, G, B, A interleavedBuffer.flip;

// Create a new VAO int vaoID = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoID);

// Create a new VBO for our interleaved data int interVboID = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, interVboID); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleavedBuffer, GL15.GL_STATIC_DRAW);

// -- We'll need to know some numbers beforehand, I'm using variables for these // -- so it's easier to see where they come from // There are 4 bytes in a float int floatByteSize = 4; // We use 3 floats for our position int positionFloatCount = 3; // We use 4 floats for our color int colorFloatCount = 4; // So the total amount of floats used is ... int floatsPerVertex = positionFloatCount + colorFloatCount; // So the total amount of bytes per vertex used is (this is the 'stride') ... int vertexFloatSizeInBytes = floatByteSize * floatsPerVertex;

// -- Now we can split our interleaved data over 2 attribute lists // First up is our positional information in list 0 GL20.glVertexAttribPointer(0, positionFloatCount, GL11.GL_FLOAT, false, 		vertexFloatSizeInBytes, 0); // Second is our color information in list 1, for this we also need the offset int byteOffset = floatByteSize * positionFloatCount; GL20.glVertexAttribPointer(1, colorFloatCount, GL11.GL_FLOAT, false, 		vertexFloatSizeInBytes, byteOffset);

GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL30.glBindVertexArray(0);

That’s it, what we have done is put all information in one big array and said to OpenGL that the information for one vertex spans over a certain amount of bytes. Even more we’ve said that all the bytes for one vertex can be also split up, attribute list 0 must only look at the first X amount of bytes. While attribute list 1 must only look at the Y amount of bytes following the previous X. We do this by setting out “stride” and “offset”.
 * The stride tells OpenGL how many bytes should be grouped together. In essence it splits our buffer up in smaller buffers. The length of these (imaginary) smaller buffers is the stride value itself.
 * When working with multiple lists that work with the same buffer we can also tell OpenGL what the offset is in the stride for that list. This offset is also a number of bytes, so if the previous list had 3 items each having 4 bytes. The offset for the next list will be 12 bytes + whatever offset the previous list already had (assuming they are all using the same buffer)

Graphically it looks like this:



The entire buffer is split up in smaller segments, the size is the stride. This segment can be segmented again in the different groups of bytes, where each group defines a different property of the vertex. The previous image is a visual representation of the buffer used in the former examples where the position property had 3 floats (one float is 4 bytes) and the colors had 4 floats.

Practical management of vertices
When adding different properties to a vertex we quickly get a lot of different float arrays and other variables. To avoid having to manage all these buffers and arrays one by one it’s better to handle it more “object oriented” we’re using Java after all. So as a first improvement we’re going to create a vertex class that holds our different properties namely our position and our color. It will also hold a few numbers so it’s easier when setting up the attribute lists.

 public class Vertex { // Vertex data private float[] xyzw = new float[] {0f, 0f, 0f, 1f}; private float[] rgba = new float[] {1f, 1f, 1f, 1f}; // The amount of elements that a vertex has public static final int elementCount = 8; // The amount of bytes an element has public static final int elementBytes = 4; // The size of a vertex in bytes, like in C/C++: sizeof(Vertex) public static final int sizeInBytes = elementBytes * elementCount; // Setters public void setXYZ(float x, float y, float z) { this.setXYZW(x, y, z, 1f); }	public void setRGB(float r, float g, float b) { this.setRGBA(r, g, b, 1f); }	public void setXYZW(float x, float y, float z, float w) { this.xyzw = new float[] {x, y, z, w}; }	public void setRGBA(float r, float g, float b, float a) { this.rgba = new float[] {r, g, b, 1f}; }	// Getters public float[] getXYZW { return new float[] {this.xyzw[0], this.xyzw[1], this.xyzw[2], this.xyzw[3]}; }	public float[] getRGBA { return new float[] {this.rgba[0], this.rgba[1], this.rgba[2], this.rgba[3]}; } }

Now that we’ve got a Vertex class it’s easier to assign colors and positions. Let’s see how we can recreate our quad using these Vertex objects:

 // We'll define our quad using 4 vertices of the custom 'Vertex' class Vertex v0 = new Vertex; v0.setXYZ(-0.5f, 0.5f, 0f); v0.setRGB(1, 0, 0); Vertex v1 = new Vertex; v1.setXYZ(-0.5f, -0.5f, 0f); v1.setRGB(0, 1, 0); Vertex v2 = new Vertex; v2.setXYZ(0.5f, -0.5f, 0f); v2.setRGB(0, 0, 1); Vertex v3 = new Vertex; v3.setXYZ(0.5f, 0.5f, 0f); v3.setRGB(1, 1, 1);

And now we simply create a new “FloatBuffer” and for each “Vertex” put the information in the buffer:

 Vertex[] vertices = new Vertex[] {v0, v1, v2, v3}; // Put each 'Vertex' in one FloatBuffer FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length *		Vertex.elementCount); for (int i = 0; i < vertices.length; i++) { verticesBuffer.put(vertices[i].getXYZW); verticesBuffer.put(vertices[i].getRGBA); } verticesBuffer.flip;

Now for my question from before, when using “glDrawElements” we have to create a new VBO that has the “element” order or indices. This is not changed from when we were not interleaving the order is still the same:

 // OpenGL expects to draw vertices in counter clockwise order by default byte[] indices = { 0, 1, 2,		2, 3, 0 };

Now we have to do our VAO and VBO work:

 // Create a new Vertex Array Object in memory and select it (bind) vaoId = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoId);

// Create a new Vertex Buffer Object in memory and select it (bind) vboId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW); // Put the positions in attribute list 0 GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 0); // Put the colors in attribute list 1 GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 		Vertex.elementBytes * 4); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);

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

No further changes are required. We’re still using 2 attribute lists, the thing that changed is that we’re using data from 1 VBO for 2 lists.

The result
The look of our quad has not changed, we’re simply changing the data layout internally.



Complete source code
 import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.nio.ByteBuffer; 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 TheQuadExampleInterleaved { // Entry point for the application public static void main(String[] args) { new TheQuadExampleInterleaved; }	// Setup variables private final String WINDOW_TITLE = "The Quad: Interleaved"; private final int WIDTH = 320; private final int HEIGHT = 240; // Quad variables private int vaoId = 0; private int vboId = 0; private int vboiId = 0; private int indicesCount = 0; // Shader variables private int vsId = 0; private int fsId = 0; private int pId = 0; public TheQuadExampleInterleaved { // Initialize OpenGL (Display) this.setupOpenGL; this.setupQuad; this.setupShaders; 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); }	public void setupQuad { // We'll define our quad using 4 vertices of the custom 'Vertex' class Vertex v0 = new Vertex; v0.setXYZ(-0.5f, 0.5f, 0f); v0.setRGB(1, 0, 0); Vertex v1 = new Vertex; v1.setXYZ(-0.5f, -0.5f, 0f); v1.setRGB(0, 1, 0); Vertex v2 = new Vertex; v2.setXYZ(0.5f, -0.5f, 0f); v2.setRGB(0, 0, 1); Vertex v3 = new Vertex; v3.setXYZ(0.5f, 0.5f, 0f); v3.setRGB(1, 1, 1); Vertex[] vertices = new Vertex[] {v0, v1, v2, v3}; // Put each 'Vertex' in one FloatBuffer FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(vertices.length *				Vertex.elementCount); for (int i = 0; i < vertices.length; i++) { verticesBuffer.put(vertices[i].getXYZW); verticesBuffer.put(vertices[i].getRGBA); }		verticesBuffer.flip; // OpenGL expects to draw vertices in counter clockwise order by default byte[] indices = { 0, 1, 2,				2, 3, 0		};		indicesCount = indices.length; ByteBuffer indicesBuffer = BufferUtils.createByteBuffer(indicesCount); indicesBuffer.put(indices); indicesBuffer.flip; // Create a new Vertex Array Object in memory and select it (bind) vaoId = GL30.glGenVertexArrays; GL30.glBindVertexArray(vaoId); // Create a new Vertex Buffer Object in memory and select it (bind) vboId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, verticesBuffer, GL15.GL_STATIC_DRAW); // Put the positions in attribute list 0 GL20.glVertexAttribPointer(0, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 0); // Put the colors in attribute list 1 GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, Vertex.sizeInBytes, 				Vertex.elementBytes * 4); GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); // Deselect (bind to 0) the VAO GL30.glBindVertexArray(0); // Create a new VBO for the indices and select it (bind) - INDICES vboiId = GL15.glGenBuffers; GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId); GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL15.GL_STATIC_DRAW); GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); }	private void setupShaders { int errorCheckValue = GL11.glGetError; // Load the vertex shader vsId = this.loadShader("src/thequad/vertex.glsl", GL20.GL_VERTEX_SHADER); // Load the fragment shader fsId = this.loadShader("src/thequad/fragment.glsl", GL20.GL_FRAGMENT_SHADER); // Create a new shader program that links both shaders pId = GL20.glCreateProgram; GL20.glAttachShader(pId, vsId); GL20.glAttachShader(pId, fsId);

// Position information will be attribute 0 GL20.glBindAttribLocation(pId, 0, "in_Position"); // Color information will be attribute 1 GL20.glBindAttribLocation(pId, 1, "in_Color"); GL20.glLinkProgram(pId); GL20.glValidateProgram(pId); errorCheckValue = GL11.glGetError; if (errorCheckValue != GL11.GL_NO_ERROR) { System.out.println("ERROR - Could not create the shaders:" + GLU.gluErrorString(errorCheckValue)); System.exit(-1); }	}	public void loopCycle { GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); GL20.glUseProgram(pId); // Bind to the VAO that has all the information about the vertices GL30.glBindVertexArray(vaoId); GL20.glEnableVertexAttribArray(0); GL20.glEnableVertexAttribArray(1); // Bind to the index VBO that has all the information about the order of the vertices GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboiId); // Draw the vertices GL11.glDrawElements(GL11.GL_TRIANGLES, indicesCount, GL11.GL_UNSIGNED_BYTE, 0); // Put everything back to default (deselect) GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); GL20.glDisableVertexAttribArray(0); GL20.glDisableVertexAttribArray(1); GL30.glBindVertexArray(0); GL20.glUseProgram(0); }	public void destroyOpenGL { // Delete the shaders GL20.glUseProgram(0); GL20.glDetachShader(pId, vsId); GL20.glDetachShader(pId, fsId); GL20.glDeleteShader(vsId); GL20.glDeleteShader(fsId); GL20.glDeleteProgram(pId); // Select the VAO GL30.glBindVertexArray(vaoId); // Disable the VBO index from the VAO attributes list GL20.glDisableVertexAttribArray(0); GL20.glDisableVertexAttribArray(1); // Delete the vertex VBO GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboId); // Delete the index VBO GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0); GL15.glDeleteBuffers(vboiId); // Delete the VAO GL30.glBindVertexArray(0); GL30.glDeleteVertexArrays(vaoId); Display.destroy; }	public int loadShader(String filename, int type) { StringBuilder shaderSource = new StringBuilder; int shaderID = 0; try { BufferedReader reader = new BufferedReader(new FileReader(filename)); String line; while ((line = reader.readLine) != null) { shaderSource.append(line).append("\n"); }			reader.close; } catch (IOException e) { System.err.println("Could not read file."); e.printStackTrace; System.exit(-1); }		shaderID = GL20.glCreateShader(type); GL20.glShaderSource(shaderID, shaderSource); GL20.glCompileShader(shaderID);

if (GL20.glGetShader(shaderID, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) { System.err.println("Could not compile shader."); System.exit(-1); }		return shaderID; } }

Credit
Mathias Verboven (moci)