user8221752
user8221752

Reputation:

How to initialize matrices in a shader storage buffer?

I have a shader storage block in the vertex shader, like this:

layout(std430,binding=0) buffer buf {mat3 rotX, rotY, rotZ; } b;

I initialized those 3 matrices with identity matrix like this:

float mats[]={  1,0,0,0,1,0,0,0,1,
                1,0,0,0,1,0,0,0,1,
                1,0,0,0,1,0,0,0,1   };
GLuint ssbos;
glGenBuffers(1,&ssbos);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER,0,ssbos);
glBufferData(GL_SHADER_STORAGE_BUFFER,sizeof(mats),mats,GL_DYNAMIC_DRAW);

But it doesn't seem to work (I'm using Opengl 4.3 core profile). Am I doing something wrong?

Upvotes: 1

Views: 2180

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473407

glBindBufferBase(GL_SHADER_STORAGE_BUFFER,0,ssbos);
glBufferData(GL_SHADER_STORAGE_BUFFER,sizeof(mats),mats,GL_DYNAMIC_DRAW);

glBindBufferBase binds the entire range of the buffer. But it's not a magic "bind whatever the buffer happens to store" function. It binds the entire range of the buffer as it currently exists.

And since you haven't allocated any storage for that buffer object, its current state is empty: a size of 0. And that's what you bind: a range of 0 bytes of memory.

Oh sure, in the next statement, you give the buffer memory. But that doesn't change the fact that it didn't have memory when you bound it.

So you need to create storage for the buffer before binding a range of it.

Also, don't use vec3 or any types related to vec3 in buffer-backed interface blocks. And you really shouldn't be passing axial rotation matrices like that.

Upvotes: 2

bernie
bernie

Reputation: 10390

The std430 layout is essentially std140 with tighter packing of structs and arrays. The data you are supplying does not respect the layout rules.

From section 7.6.2.2 Standard Uniform Block Layout of the OpenGL spec:

  1. If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element, according to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The array may have padding at the end; the base offset of the member following the array is rounded up to the next multiple of the base alignment.
  2. If the member is a column-major matrix with C columns and R rows, the matrix is stored identically to an array of C column vectors with R components each, according to rule (4).

So your mat3 matrices are treated as 3 vec3 each (one for each column). According to the rule (4), a vec3 is padded to occupy the same memory as a vec4.

In essence, when using a mat3 in an SSBO, you need to supply the same amount of data as if you were using a mat4 mat3x4 with the added benefit of a more confusing memory layout. Therefore, it is best to use mat3x4 (or mat4) in an SSBO and only use its relevant portions in the shader. Similar advice also stands for vec3 by the way.

It is easy to get smaller matrices from a larger one:

A wide range of other possibilities exist, to construct a matrix from vectors and scalars, as long as enough components are present to initialize the matrix. To construct a matrix from a matrix:

mat3x3(mat4x4); // takes the upper-left 3x3 of the mat4x4
mat2x3(mat4x2); // takes the upper-left 2x2 of the mat4x4, last row is 0,0
mat4x4(mat3x3); // puts the mat3x3 in the upper-left, sets the lower right
                // component to 1, and the rest to 0

This should give you proper results:

float mats[]={  1,0,0,0, 0,1,0,0, 0,0,1,0,
                1,0,0,0, 0,1,0,0, 0,0,1,0,
                1,0,0,0, 0,1,0,0, 0,0,1,0,  };

Upvotes: 0

Related Questions