glsl 150 struct in uniform buffer object

Can I do this in opengl 3.2 / glsl 150?

struct LightSource {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
vec4 direction;
};
...
layout(std140) uniform LightSources {
LightSource lightSources[ 1 ];
};
...


And if so, how do I actually bind the data (using c++)?

I've got data binding working for this uniform:

layout(std140) uniform LightSources {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
vec4 direction;
};


I'm just not sure how to extend it to use an array of structs instead of binding 5 vec4s.

• It's not clear what your question is. Are you asking how std140 layout works? Or what? – Nicol Bolas Feb 26 '13 at 7:18
• Hmm...no, not specifically about std140, but rather, how to pass data to a ubo struct - but I see what you mean, using std140 I just basically use the same c++ code to transfer the data as long as the layout is the same for the data in c++ and glsl...I think that's right. – Jarrett Feb 27 '13 at 15:55

This is explained at http://ogldev.atspace.co.uk/www/tutorial20/tutorial20.html about halfway down the page.

I would've done a comment, but not enough rep :(

Edit: In order to make up for my bad answer, I have found the pertinent sections of the OpenGL 3.2 specification. I hope this helps.

When using the std140 storage layout, structures will be laid out in buffer storage with its members stored in monotonically increasing order based on their location in the declaration. A structure and each structure member have a base offset and a base alignment, from which an aligned offset is computed by rounding the base offset up to a multiple of the base alignment. The base offset of the ﬁrst member of a structure is taken from the aligned offset of the structure itself. The base offset of all other structure members is derived by taking the offset of the last basic machine unit consumed by the previous member and adding one. Each structure member is stored in memory at its aligned offset. The members of a toplevel uniform block are laid out in buffer storage by treating the uniform block as a structure with a base offset of zero.

If the member is a structure, the base alignment of the structure is N, where N is the largest base alignment value of any of its members, and rounded up to the base alignment of a vec4. The individual members of this substructure are then assigned offsets by applying this set of rules recursively, where the base offset of the ﬁrst member of the sub-structure is equal to the aligned offset of the structure. The structure may have padding at the end; the base offset of the member following the sub-structure is rounded up to the next multiple of the base alignment of the structure.

If the member is an array of S structures, the S elements of the array are laid out in order, according to rule (9) (the last paragraph).

Edit 2: It seems from the specification that arrays of structs are lined up directly next to each other in the buffer. So if you had 2 lights, the buffer data would just be the 20 floats defining that light, followed by the 20 floats defining the next light.

The reason it is that simple is because all of your struct members are the same type. If you had a vec2 in there, you would have to skip 2 floats in the buffer.

Edit 3: Sorry for all of these edits. I tested what I said in edit 2, and it indeed worked. I created a similar structure to yours in my shaders, but I used 3 vec4's for simplicity. I had an interface uniform containing an array of 2 of those structs. In my uniform buffer object I just placed 24 floats (4 floats per vec4 * 3 vec4 per struct * 2 structs) After binding the object I was able to read the correct values in the shader.

• My mistake. I just noticed it looked like he was trying to do the same thing as the tutorial. I've seen people respond with links in comments before, but I can't comment so I posted as an answer. I figured it was better than nothing. – kfan Feb 26 '13 at 8:29
• I added an actual answer now. Hopefully it's up to snuff. – kfan Feb 26 '13 at 9:47
• Thanks @kfan - basically, all I had to do was switch the glsl code, and the c++ code I had to transfer the data to the graphics code didn't have to changed at all :) – Jarrett Feb 27 '13 at 15:53