12
$\begingroup$

Is it possible to have a dynamic array in a GLSL shader? For instance, what if I have something like this in my GLSL Shader:

uniform int size;
uniform SceneLights lights[size];

void main()
{
    for(int i = 0; i < size; i++) {
        /* Do Some Calculation */
    }
}

And this would be my C++ file:

for (GLuint i = 0; i < pointLights.size(); i++) {
    glUniform3f(glGetUniformLocation(shader, ("pointLights[" + std::to_string(i) + "].position").c_str()), lights[i].someValue);
}

I would like to do this because I have a file with the positions of all the light sources in my scene (this may vary for each scene) so I would like to have a dynamic array which will allow me to send different numbers of lights to my shader.

$\endgroup$
1
  • 1
    $\begingroup$ There are also Shader Storage Buffer Objects that you can read about here: khronos.org/opengl/wiki/Shader_Storage_Buffer_Object And in vulkan there are "unbounded arrays" that are declared in GLSL like this: layout(binding=10) uniform sampler2D TextureUBA[];" (sampler2D is just an example) I'm not sure how that is set up in OpenGL though. The last version has very large limits but both of these have a performance penalty which varies depending on how the memory is accessed. $\endgroup$
    – pmw1234
    Commented Dec 23, 2020 at 23:24

3 Answers 3

21
$\begingroup$

I don't think uniform arrays can be dynamically sized. In your case you should define the array as the maximum number of lights you will process and then use a uniform to control the number of iterations you do on this array. On the CPU side you can set a subset of the lights[] array according to the 'size' variable.

e.g.

#define MAX_LIGHTS 128

uniform int size;
uniform SceneLights lights[MAX_LIGHTS];

void main()
{
    for(int i = 0; i < size; i++) {
        /* Do Some Calculation */
    }
}

I think (speculate) the likely reason for this is that it would be impossible to determine the location of a uniform if there are variable sized arrays in your uniform list which depend on the value of another uniform.. e.g.

uniform int arraySize;               // offset 0
uniform int myArray[arraySize];      // offset 4
uniform int anotherVar;              // offset ???? 

GLSL would not know were to place anotherVar, because it would need arraySize to be set with a value before it can compute the offset of 'anotherVar'. I suppose the compiler could be clever and re-arrange the order of uniforms to get around this, but this will fail again if you have 2 or more variable sized arrays..

$\endgroup$
1
  • $\begingroup$ +1 especially for stating the reason you think it's not possible, and of course for offering an alternative strategy. Very helpful answer. $\endgroup$
    – Alexandre
    Commented Nov 27, 2021 at 22:21
4
$\begingroup$

I am suprised that it was only mentioned in comments but yes you can use variable length arrays through SSBO. Here is the excerpt from wiki:

On the host side:

int data[SOME_SIZE];
...
GLuint ssbo;
glGenBuffers(1, &ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(data), data​, GLenum usage); //sizeof(data) only works for statically sized C/C++ arrays.
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind

On the glsl side:

layout(std430, binding = 3) buffer layoutName
{
    int data_SSBO[];
};

Note that it has to be the last element in an SSBO:

layout(std430, binding = 2) buffer anotherLayoutName
{
    int some_int;
    float fixed_array[42];
    float variable_array[];
};

The only limitation is that you can use one variable array per SSBO. You can use multiple SSBO's in a shader though.

$\endgroup$
0
3
$\begingroup$

You could add the variable as code when you upload the shader code.

char header[64];
sprintf (header, "const int size = %u;\n", pointLights.size());

Then add the header (assuming your code is in char code[]):

GLchar  *source[2] = {(GLchar  *)header, (GLchar  *)code};
glShaderSource(shader, 2, (const GLchar **)source, NULL);
..

That way you can vary the size and add it to the shader on the fly. Bit of a hack, but it's the way I did it.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.