How do I properly declare a uniform array of structs in GLSL so that I can point a UBO at it? - opengl

The following glsl code appears in my fragment shader. The struct definition causes no problems, but my attempt to use it as the type of a uniform array causes an "invalid operation" error, which is not particularly helpful.
struct InstanceData
{
vec3 rotation;
vec3 scale;
mat4 position;
};
layout (std140) uniform InstanceData instances[100];
How do I structure this code correctly so that it compiles without error, and is thus ready for me to populate with data? Note that I am using core profile version 4.5.
Edit: It seems to be something to do with the use of layout (std140). Removal of that part allows the code to compile, though don't I need that to ensure that the glsl compiler packs the struct data in a predictable way?
Edit: Still not working. My entire vertex shader code looks like this:
#version 450
layout(location=0) in vec4 in_Position;
layout(location=1) in vec4 in_Color;
out vec4 ex_Color;
flat out int ex_Instance;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
// ------ preliminary addition of uniform block to be used soon ------
struct layout (std140) InstanceData
{
vec3 rotation;
vec3 scale;
mat4 position;
};
layout (std140, binding = 0) uniform InstanceData
{
InstanceData instances[100];
};
// -------------------------------------------------------------------
void main(void)
{
gl_Position = (projectionMatrix * viewMatrix * modelMatrix) * in_Position;
ex_Color = in_Color;
}
Note that I haven't written any code externally to populate the uniform buffer yet, nor, as you can see above, have I adjusted my code to make use of the data either. I just want it to compile and work initially as-is (i.e. declared but not used), at which point I will add additional code to start making use of it. There's no point going even that far if the program doesn't like the declaration to begin with.
Edit: Finally figured out the problem by using the shader info log, like so:
GLint infoLogLength;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar* strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(id, infoLogLength, NULL, strInfoLog);
In a nutshell, as Anton said, I was using layout incorrectly, and my uniform block had the same name as my struct, creating all kinds of confusion for the compiler.

To do this portably, you should use a Uniform Buffer Object.
Presently, your struct uses 3+3+16=22 floating-point components and you are trying to build an array of 100 of these. OpenGL implementations are only required to support 1024 floating-point uniform components in any stage, and your array requires 2200.
Uniform Buffer Objects will allow you to store up to 64 KiB (minimum) of data; well exceeding the limitation above. However, you need to be mindful of data alignment when using UBOs and that is what the layout (std140) qualifier you tried to use is for.
struct InstanceData
{
vec3 rotation;
vec3 scale;
mat4 position;
};
// Uniform block named InstanceBlock, follows std140 alignment rules
layout (std140, binding = 0) uniform InstanceBlock {
InstanceData instances [100];
};
The struct above is not correctly aligned for std140, you will need to be careful when using it.
This is how the data is actually laid out:
struct InstanceData
{
vec3 rotation; // 0,1,2
float padding03; // 3
vec3 scale; // 4,5,6
float padding07; // 7
mat4 position; // 8-23
} // Size: 24 * sizeof (float)
vec3 types are treated the same as vec4 in GLSL and mat4 is effectively an array of 4 vec4, so that means they all need to begin on 4-float boundaries. GLSL automatically inserts padding to satisfy these alignment rules; the changes I made above are to show you the correct way to write this data structure in C. You have to account for 2 floats worth of implicit padding in your structure.
Regarding your edit, you do not have to worry about how GLSL packs a struct until you start using buffer objects.
Without using a Uniform Buffer Object, you have to use functions like glUniform3f (...) to set uniforms. Those functions do not directly expose the data structure to you, so packing does not matter. To set the values for an instance N you would call glUniform3f (...) using the location of "instances [N].rotation" and "instances [N].scale" and glUniformMatrix4fv (...) on "instances [N].position".
That would require 300 API calls to initialize all 100 instances of your struct, so you can see why UBOs are more practical (even ignoring the above mentioned 1024 limitation).

Related

how can i load an array of uniform buffer objects into the shader?

shader code:
// UBO for MVP matrices
layout (binding = 0) uniform UniformBufferObject {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
this works fine because its just one struct and I can set the VkWriteDescriptorSet.descriptorCount to 1. But how can I create an array of those structs?
// Want to do something like this
// for lighting calculations
layout (binding = 2) uniform Light {
vec3 position;
vec3 color;
} lights[4];
I have the data for all of the four lights stored in one buffer. When I set the VkWriteDescriptorSet.descriptorCount to four,
Do I have to create four VkDescriptorBufferInfo? If so, I dont know what to put into offset and range.
All of the blocks in a uniform buffer array live in the same descriptor. However, they are still different blocks; they get a different VkDescriptorBufferInfo info object. So those blocks don't have to come from sequential regions of storage.
Note: The KHR_vulkan_glsl extension gets this wrong, if you look pay close attention. It notes that arrays of opaque types should go into a single descriptor, but arrays of interface blocks don't. The actual glslangValidator compiler (and SPIR-V and Vulkan) do handle it as I described.
However, you cannot access the elements of an interface block array with anything other than dynamically uniform expressions. And even that requires having a certain feature available; without that feature, you can only access the array with constant expressions.
What you probably want is an array within the block, not an array of blocks:
struct Light
{
vec4 position; //(NEVER use `vec3` in blocks)
vec4 color;
};
layout (set = 0, binding = 2, std140) uniform Lights {
Light lights[4];
};
This means that you have a single descriptor in binding slot 2 (descriptorCount is 1). And the buffer's data should be 4 sequential structs.

Acessing VBO/VAO Data in GLSL Shader

In a vertex shader how can a function within the shader be made to access a specific attribute array value after buffering its vertex data to a VBO?
In the shader below the cmp() function is supposed to compare a uniform variable with vertex i.
#version 150 core
in vec2 vertices;
in vec3 color;
out vec3 Color;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec2 cmp_vertex; // Vertex to compare
out int isEqual; // Output variable for cmp()
// Comparator
vec2 cmp(){
int i = 3;
return (cmp_vertex == vertices[i]);
}
void main() {
Color = color;
gl_Position = projection * view * model * vec4(vertices, 0.0, 1.0);
isEqual = cmp();
}
Also, can cmp() be modified so that it does the comparison in parallel?
Based on the naming in your shader code, and the wording of your question, it looks like you misunderstood the concept of vertex shaders.
The vertex shader is invoked once for each vertex. So when your vertex shader code executes, it always operates on a single vertex. This means that the name of your in variable is misleading:
in vec2 vertices;
This variable gives you the position of the one and only vertex your shader is working on. So it would probably be clearer if you used a name in singular form:
in vec2 vertex;
Once you realize that you're operating on a single vertex, the rest becomes easy. For the comparison:
bool cmp() {
return (cmp_vertex == vertex);
}
Vertex shaders are typically already invoked in parallel, meaning that many instances can execute at the same time, each one on its own vertex. So there is no need for parallelism within a single shader instance.
You'll probably have more issues achieving what you're after. But I hope that this gets you at least over the initial hurdle.
For example, the following out variable is problematic:
out int isEqual;
out variables of the vertex shader have matching in variables in the fragment shader. By default, the value written by the vertex shader is linearly interpolated across triangles, and the fragment shader gets the interpolated values. This is not supported for variables of type int. They only support flat interpolation:
flat out int isEqual;
But this will probably not give you what you're after, since the value you see in the fragment shader will always be the same across an entire triangle.

Using input/output structs in GLSL-Shaders

In HLSL I can write:
struct vertex_in
{
float3 pos : POSITION;
float3 normal : NORMAL;
float2 tex : TEXCOORD;
};
and use this struct as an input of a vertex shader:
vertex_out VS(vertex_in v) { ... }
Is there something similar in GLSL? Or do I need to write something like:
layout(location = 0) in vec4 aPosition;
layout(location = 1) in vec4 aNormal;
...
What you are looking for are known as interface blocks in GLSL.
Unfortunately, they are not supported for the input to the Vertex Shader stage or for the output from the Fragment Shader stage. Those must have explicitly bound data locations at program link-time (or they will be automatically assigned, and you can query them later).
Regarding the use of HLSL semantics like : POSITION, : NORMAL, : TEXCOORD, modern GLSL does not have anything like that. Vertex attributes are bound to a generic (numbered) location by name either using glBindAttribLocation (...) prior to linking or as in your example, layout (location = ...).
For input / output between shader stages, that is matched entirely on the basis of variable / interface block name during GLSL program linking (except if you use the relatively new Separate Shader Objects extension). In no case will you have constant pre-defined named semantics like POSITION, NORMAL, etc. though; locations are all application- or linker-defined numbers.

Uniform Buffer Object not aligned. GLSL access broken

Using GLFW, GLEW (Although it shouldn't matter)
When passing a struct to the fragment shader, unexpected behavior is observed:
If I pass the ambient color directly:
out vec3 color
...
color = MaterialCols.ambient
Everything works as expected. When passing diffuse, it is offset by 1 (i.e. the green channel is red, the blue channel is green)
When passing specular, the first value is the green channel of the previous vec3 (diffuse).
I believe this is an issue of alignment. It seems either that GLSL only wants to access vec3 components on a specific alignment, or that the C++ code is adding some padding (for reasons unkown).
This is the data allocation. It is passed as a uniform buffer object. If required, I can add the code used.
C++:
typedef struct material_colors{
glm::vec3 ambient;
glm::vec3 diffuse;
glm::vec3 specular;
}material_cols;
GLSL:
uniform MaterialColors {
vec3 ambient;
vec3 diffuse;
vec3 specular;
} MaterialCols;
As per derhass' comment, and the related section 2.11.4 titled "Uniform Variables"in the OpenGL 3.2 Core specifications:
3. If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.
So, by padding the c++ struct using a float after the first vec3, brings the diffuse into alignment.
C++
typedef struct material_colors{
glm::vec3 ambient;
float padding;
glm::vec3 diffuse;
glm::vec3 specular;
}material_cols;

GLSL fragment shader struct out

i have a problem with my GLSL shader.
When i want to compile the fragment shader part, i get the following error:
0:24: error(#181) Cannot be used with a structure: out
error(#273) 1 compilation errors. No code generated
So the problem lies around my out variable, i assume.
Here is my fragment shader:
#version 410
uniform mat4 gWVP;
uniform mat4 gWorld;
uniform sampler2D gColorMap;
in VSOutput
{
vec3 WorldSpacePos;
vec2 TexCoord;
vec3 Normal;
} FSin;
struct FSOutput
{
vec3 WorldSpacePos;
vec3 Diffuse;
vec3 Normal;
vec3 TexCoord;
};
out FSOutput FSOut;
void main()
{
FSOut.WorldSpacePos = FSin.WorldSpacePos;
FSOut.Diffuse = texture(gColorMap, FSin.TexCoord).xyz;
FSOut.Normal = normalize(FSin.Normal);
FSOut.TexCoord = vec3(FSin.TexCoord, 0.0);
}
As i know it should be possible to output structs in OpenGL 4.0+, shouldn't it? So I dont get the error, is it a driver problem or something like that?
I'm running on a Radeon HD 6950 with 13.4 drivers.
As i know it should be possible to output structs in OpenGL 4.0+, shouldn't it?
No, it shouldn't.
The GLSL specification is quite clear on this: vertex shader inputs and fragment shader outputs cannot be structs. From the GLSL 4.4 specification, section 4.3.6:
Fragment outputs can only be float, single-precision floating-point vectors, signed or unsigned integers or integer vectors, or arrays of any these. It is a compile-time error to declare any double-precision type, matrix, or structure as an output.
They also can't be aggregated into interface blocks, in case you're wondering. They must be loose variables.