Journal Entry 22 – Uniform blocks

I’ve had a couple of busy days, and have just now gotten some time to sit down and write a bit of code. I’m pretty tired, so I only got a bit done, but I thought I would quickly share it before heading to sleep.

I have several shaders that I am playing around with, and they each contain a project_matrix uniform, and a view_matrix uniform. Any time the camera moves I have to update the view_matrix (and sometimes the projection matrix). This is a pain, because I have to keep track of every shader that references the camera matrices, and then loop through them, first binding them and then setting the uniforms. Luckily, there is a cool way to avoid this by using uniform blocks. There’s a solid article on them here: GLSL Core Tutorial – Uniform Blocks

The first thing I had to do was update my OpenGL library to include some of the methods we’ll need. I added the experimental support for OpenGL 4 methods that weren’t part of Tao or Open TK. They should all work, as their calling convention was created using the OpenGL documentation as a reference. However, I haven’t tested all of them!

Next we modify our shader code to use a uniform block instead of two separate uniforms. Here’s the updated shader code:

uniform Camera {
   mat4 projection_matrix;
   mat4 view_matrix;
};

I now have a uniform block named ‘Camera’ which I can update. I just need to bind this uniform block to a binding point that points to some sort of valid buffer data. To bind this uniform block to a binding point we first have to get its location in the shader, and then bind it. I chose binding point 1 for my camera uniform block.

uint blockIndex = (uint)Gl.GetUniformBlockIndex(program.ProgramID, "Camera");
Gl.UniformBlockBinding(program.ProgramID, blockIndex, 1);

Note: You’ll have to call this code once for each shader that has the camera uniform block. This is to let the program know which binding point the camera uniform block should use. They will all point to binding point 1, so when we update the data there, all of the shaders will update in unison. Pretty cool!

The final step is to actually create a buffer that contains the camera data. We need to do this whenever the camera is updated.

private static uint cameraBuffer = 0;
 
private static void BindCameraBuffer()
{
    // copy both the projection matrix and the view matrix to a float[]
    float[] data = new float[4 * 4 * 2];
    projectionMatrix.ToFloat().CopyTo(data, 0);
    camera.ViewMatrix.ToFloat().CopyTo(data, 16);
 
    // delete the camera buffer if it already exits, as we're creating a new one
    if (cameraBuffer != 0) Gl.DeleteBuffers(1, new uint[] { cameraBuffer });
 
    // create the buffer that contains the camera data
    cameraBuffer = Gl.CreateVBO<float>(BufferTarget.UniformBuffer, data, BufferUsageHint.DynamicDraw);
 
    // bind the newly created buffer to binding point 1
    // which is the binding point used by all of our shaders
    Gl.BindBufferBase(BufferTarget.UniformBuffer, 1, cameraBuffer);
}

That’s all there is to it! I just have to call BindCameraBuffer() every frame that the camera is updated, and all of the shaders that have a uniform block pointing to binding point 1 will be updated. Since I promised no more screenshots of falling voxels, here’s a screenshot of a tree I hastily put together. This is using the new uniform block code. It hasn’t yielded much of a performance boost or anything, but makes writing new shaders a lot easier since I don’t have to worry about always updating their uniforms. Less work for me!

I didn't think you all would be very impressed if I posted another screenshot of falling voxels.  So, instead I present a tree!
I didn’t think you all would be very impressed if I posted another screenshot of falling voxels. So, instead I present a tree!

This all started because I wrote a new shader for the characters that will occupy the world! Yup, I’m actually working on something aside from optimizing the voxel engine 😀 I hope to have some screenshots of characters soon!

Happy coding,

Giawa

Edit: NVidia drivers are picky and require that you enable your shader program before calling GetUniformBlockIndex. Something like this:

program.Use();
uint blockIndex = (uint)Gl.GetUniformBlockIndex(program.ProgramID, "Camera");

If you don’t have a program bound, then the driver unhelpfully crashed the program. Super deluxe!

One thought on “Journal Entry 22 – Uniform blocks

Leave a Reply