84

I would like to leave OpenGL's lights and make my own. I would like my shaders to allow for a variable number of lights.

Can we declare an array of uniforms in GLSL shaders? If so, how would we set the values of those uniforms?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Miles
  • 1,858
  • 1
  • 21
  • 34
  • 5
    Anyone who wants a variable number of lights, just make arrays with decent capacities, and keep a count of how many lights are currently active. GLSL does not allow variable-sized arrays (all array sizes must be constant, hard-coded values). – Miles May 09 '14 at 17:55

2 Answers2

116

Yes this is possible. You declare uniform arrays similar to how you'd do it in C, e.g.

uniform float v[10];

Then you can set their values using glUniform{1,2,3,4}{f,i}v

GLfloat v[10] = {...};
glUniform1fv(glGetUniformLocation(program, "v"), 10, v);
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • 2
    what does the 1, 2, 3, 4 refer to? Does that mean I can only set the values of an array up to four members? – Miles Nov 11 '11 at 21:40
  • That's a very clever/twisted use of `glUniform1fv`. One would think that the 2nd parameter of `glUniform1fv` is just a formality and that it should be used always as a 1. – bobobobo Apr 04 '13 at 16:52
  • So technically you could set a uniform `vec4` using `glUniform1fv( uniformId, 4, v ) ;` where `v` is an array of 4 floats. – bobobobo Apr 04 '13 at 16:53
  • @bobobobo: Good question. I honestly don't know (yet), but I'd not recommend it. The GLSL datatypes kind of reflect the n of glUniform – datenwolf Apr 04 '13 at 17:32
  • 20
    @bobobobo: "*So technically you could set a uniform vec4 using glUniform1fv( uniformId, 4, v ) ; where v is an array of 4 floats.*" No, you can't. The spec is very clear: the size and type *must* match the uniform's size and type (except for bools which use `i` and opaques which use `1i`). If you say `1f`, then the uniform must be 1 float. Otherwise, `GL_INVALID_OPERATION` ensues. – Nicol Bolas Jun 03 '13 at 09:11
  • 10
    @MilesRufat-Latre `{1234}` in functions like `glUniform` tells about the type of the uniform variable. 1f means a single float, 2i means ivec2 and so on. This should not be confused with the 2nd `count` parameter that the function takes which is the array size. So to upload to `uniform vec2 vecs[5];`, it'd be `glUniform2fv(loc, 5, data);`. – legends2k Nov 30 '14 at 08:07
  • shouldnt the last line be `glUniform1fv(glGetUniformLocation(program, "v"), 10, &v);` (&v instead of v) – Asad-ullah Khan Feb 04 '21 at 18:16
  • 2
    @Asad-ullahKhan: No, because of C array to pointer decay. – datenwolf Feb 04 '21 at 18:57
5

Yes it is possible to declare an array of uniforms in GLSL shaders. Just google "glsl uniform array" for some examples (edit: or see datenwolf's example). There are however limitations on how many uniforms can be sent to different graphics cards (at least on older ones, I'm not sure about current ones (although I imagine there still would be)).

If you do decide to go down the route of uniforms, i would suggest using uniform buffers. According to http://www.opengl.org/wiki/Uniform_Buffer_Object, "Switching between uniform buffer bindings is typically faster than switching dozens of uniforms in a program".

If you have large numbers of lights and parameters, you could also send the data as float buffers.

NickLH
  • 2,643
  • 17
  • 28
  • 6
    Uniform buffer objects are only available since GL version 3.1. The ARB_Uniform_Buffer_Object extension is needed since version 2.0. – Paul Wendler Sep 07 '12 at 08:18
  • 1
    You can query OpenGL at runtime to get the maximum number of uniforms with `Glint result; glGetIntegerv(GL_MAX_UNIFORM_LOCATIONS, 0, &result);` – rwols Jun 18 '13 at 09:36
  • 2
    I've google it and got on this page. Better plan in advance for such outcome when answering :) – EugZol Sep 10 '22 at 22:33