Hello Circe
The Shader Storage Buffer Object (SSBO) and the Uniform Buffer Object (UBOs) are buffer objects in OpenGL used to transfer data to shaders. Although both are very similar there are important differences between them.
The UBO provides uniform data to the shader, in the form of uniform blocks, which is accessed through internal shader-accessible memory reads. The advantage of using the UBO instead of separate uniforms is that you can quickly switch between different sets of uniform data for different instances of the same program in you application. Additionally, UBO accesses are faster than SSBOs.
SSBOs can be used in the same way, but have some advantages over UBOs:
- SSBOs can store much more memory than UBOs (
128MB
against16KB
). - shaders can write into SSBOs (we need to atomic operations and barriers though).
- SSBOs can have variable size (which can be queried by the shader at runtime).
Although SSBOs seem to be much better then UBOs, their subtle differences are important. It really depends on the situation to decide which one to use.


Let’s now discuss an example that uses both types of buffers (and gives the results above). Here we are going to setup a scene with 4 point light sources, 1 object with a PBR shader attached to it. Light data will be stored in a SSBO and PBR parameters will be stored in an UBO (the shader will access both buffers just like the two code snippets listed earlier). The steps to construct our application are as follows:
- load mesh data
- compile shader
- create and update UBO data
- create and set SSBO light data
You’ll notice that we use vec3_16
in client code to store elements in the UBO and SSBO. That is because depending on the layout you pick – std140
, std430
,… – the memory must be aligned. For example, the alignment is set to be the same as the alignment of the biggest member of the struct. For some layouts,
a 3-component vector can have its alignment rounded up to 4-component alignment, which is the case of out vec3
, so we need to use a vec3
with alignment of 16.
Scene Object⌗
Our shader will require only vertex positions and normals. We could load the mesh from a file or use some procedural mesh:
PBR Shader⌗
Here we will use a simple version of a PBR shader (source). The required uniforms are:
Note that the fragment shader will use the an UBO to access the PBR parameters. Also, you don’t need to set the location for each uniform (as above) or cache the locations in you code. Circe does it automatically.
In order to compile the shader and use it in our scene object, we can do:
To update the uniform values just do:
UBO⌗
Circe provides a class to handle UBOs:
Here we need to connect the UBO and the shader program in order to register the uniform blocks the shader will access and the binding point of the buffer:
Now we can store data into our UBO and it will be available to our shader. For this, we can declare a similar struct to represent our uniform block:
and simply store it into our buffer
SSBO⌗
Now we want to use a SSBO to store all data for the 4 lights in the scene. Remember from the beginning that the fragment shader have the definition:
We can use an array of structures to easily setup the data for our ssbo class:
and remember to bind it before draw
If we want to update our light parameters, we can map the buffer memory and modify it directly:
Putting all together⌗
Circe provides a quick way to setup a window application through a base class called BaseApp.
To inherit it, we must override the render
method:
Where
You can find the source code here.
And that is the is the result:
