Rendering

This is where things get interesting! OpenGL tries to re-use the same functons it did for vertex array rendering, but with the arguments interpreted differently. This in my opinion is a VERY bad idea, but it's how they decided to do it.

So if the functions are the same then what tells OpenGL that we want to use vertex buffers instead of arrays? The bound buffer. If a buffer is bound, OpenGL assumes you are rendering VBO's, if not it assumes you are rendering vertex arrays.

Example

Let's take a look at some code that we used to render vertex arrays:

    GL.EnableClientState(ArrayCap.VertexArray);
    GL.EnableClientState(ArrayCap.ColorArray);

    GL.VertexPointer(3, VertexPointerType.Float, 0, cubeVertices);
    GL.ColorPointer(3, ColorPointerType.Float, 0, cubeColors);

    GL.DrawElements(PrimitiveType.Triangles, cubeIndices.Length, DrawElementsType.UnsignedInt, cubeIndices);

    GL.DisableClientState(ArrayCap.VertexArray);
    GL.DisableClientState(ArrayCap.ColorArray);

Now let's convert this to use VBO's. The first thing we need to do is to enable buffers before the call to GL.VertexPointer. I'm also going to make sure to disable them once i'm done rendering. Like so:

    GL.EnableClientState(ArrayCap.VertexArray);
    GL.EnableClientState(ArrayCap.ColorArray);

    // NEW
    GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);

    GL.VertexPointer(3, VertexPointerType.Float, 0, cubeVertices);
    GL.ColorPointer(3, ColorPointerType.Float, 0, cubeColors);

    GL.DrawElements(PrimitiveType.Triangles, cubeIndices.Length, DrawElementsType.UnsignedInt, cubeIndices);

    // NEW
    GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

    GL.DisableClientState(ArrayCap.VertexArray);
    GL.DisableClientState(ArrayCap.ColorArray);

Next up we need to change the GL.VertexPointer and GL.ColorPointer functions. The only thing that changes is the last argument. Instead of being an array, it now becomes an int pointer, but it does not point to anything!

Instead of using an int-pointer we just pack an integer into the pointer If this sounds stupid, that's because it is, it's a leftover thing from OpenGL being a C-API. So this last paramater now signifies the offset from the begenning of the buffer that our data is located at.

    GL.VertexPointer(3, VertexPointerType.Float, 0, new System.IntPtr(0));
    GL.ColorPointer(3, ColorPointerType.Float, 0, new System.IntPtr(sizeof(float) * 8 * 3));

In my buffer the first 8 * 3 floats are the position of my cube. So, the cube vertices are at offset 0. But then the colors come after, so those are

8 * 3 * sizeof(float) bytes away from the front of the buffer.

Now, if we didn't have an index buffer bound, this would already render. Just because you have a vertex buffer doesn't mean you have to use an index buffer. But more often than not you will. This means that we must also change the GL.DrawElements function.

The GL.DrawElements fucntion changes the same way the others did. The last argument becomes an int-pointer. It's value is an integer, not a real pointer. This integer specifies how many bytes from the begenning of the buffer the data we are rendering is located. The new call becomes:

GL.DrawElements(PrimitiveType.Triangles, numIndices, DrawElementsType.UnsignedInt, System.IntPtr.Zero);

Putting it all together

Let's take a look at the new render function in full context

public override void Render() {
    Matrix4 lookAt = Matrix4.LookAt(new Vector3(-7.0f, 5.0f, -7.0f), new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
    GL.LoadMatrix(Matrix4.Transpose(lookAt).Matrix);

    GL.Disable(EnableCap.DepthTest);
    grid.Render();
    GL.Enable(EnableCap.DepthTest);

    GL.EnableClientState(ArrayCap.VertexArray);
    GL.EnableClientState(ArrayCap.ColorArray);

    // Bind vertex and index buffer, OpenGL will use VBO's from here on out
    GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer);

    // 0 bytes from the begenning of the buffer
    GL.VertexPointer(3, VertexPointerType.Float, 0, new System.IntPtr(0));
    // The buffer first contains positions, which are 8 vertices, made up of 3 floats each.
    // after that comes the color information, therefore the colors are:
    // 8 * 3 * sizeof(float) bytes away from the begenning of the buffer
    GL.ColorPointer(3, ColorPointerType.Float, 0, new System.IntPtr(sizeof(float) * 8 * 3));

    // The index buffer only contains indices we want to draw, so they are 0 bytes
    // from the begenning of the array. You can use the constant i use here instead
    // of making a new IntPtr, if the offset you are looking for is 0
    GL.DrawElements(PrimitiveType.Triangles, numIndices, DrawElementsType.UnsignedInt, System.IntPtr.Zero);

    // Unbind vertex and index buffers, OpenGL will draw VA's from here on out
    GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
    GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

    GL.DisableClientState(ArrayCap.VertexArray);
    GL.DisableClientState(ArrayCap.ColorArray);
}

results matching ""

    No results matching ""