Sending array to a shader

I’m trying to pass some numbers to a shader:
uniform float steps[3];
Then:
.material.setParameter(“steps”,[1,2,3]);

But it seems it is not setting anything. And returns undefined when I try:
.material.getParameter(“steps”);

Is there a special way to pass arrays?

I’ve found an old thread, but it seems inapplicable anymore.

You should read it again:

var values = new Float32Array([0, 1, 2, 3, 4, 5, 6, 7]);

There is a difference between Javascript array [1,2,3] and a typed array that uses Javascript array as input, like new Float32Array([1,2,3]). You need to pass the latter.

Didn’t work :pensive:

Perhaps you can show what you have tried?

I apologize for reviving an older thread, I had trouble myself sending an array to a shader. I was able to resolve my issue, posting my findings since there were a lot unanswered threads on the subject, in case it’s of help (@mvaligursky do step in if I got something wrong).

To make sure an array uniform is created:

  1. You need to add [0] to your uniform name, at the end.
  2. Passing Float32Array and dynamic JS arrays is supported. I think the exact length of the array is what specifies the type of the array (e.g. if it’s a float[] or vec2/vec3/vec4[] @mvaligursky I would be glad if you can verify this).
  3. All arrays should be flattened, both Float32Array or regular dynamic arrays. The length of the array should be an exact multiple of the number of elements, so if you are storing three Vec3 items in your array, the flattened array should be like this (numbers are random), code example:
// Javascript
material.setParameter("myName[0]", [0.5, 1.0, -0.5, 0.25, 1.1, -0.35, 0.75, 1.7, -0.15]); // length 3 x 3 = 9

// GLSL uniform
uniform vec3 myName[3];

// GLSL you can now use it in your vertex/pixel shader like this
for (int i = 0; i < 3; i++){
   vec3 myNameVec = myName[i];
}

If the number of the elements you pass to the array is dynamic, you still need to put a max limit of allowed elements. So declare your uniform with that limit and then you can use a uniform int myNameLength as a pointer in your for loop to avoid iterating over unused elements:

// Javascript
material.setParameter("myNameLength", myArray.length);

// GLSL dynamically change the iteration count
uniform vec3 myName[30];
uniform int myNameLength;

for (int i = 0; i < myNameLength; i++){
   vec3 myNameVec = myName[i];
}
3 Likes

I’m not sure I understand the question here. The array should be exactly as you describe in step 3.

1 Like

Note that this will likely only compile for WebGl2 devices, Webgl1 does not support dynamic loops (at least not in fragment shaders, not entirely sure about vertex shaders). The work around is to loop to some fixed max value, and break from the loop when the compare fails. Something like this:

for (int i = 0; i < 255; i++){
   if (i >= myNameLength)
      break;
   vec3 myNameVec = myName[i];
}

Thanks for the pointer Martin! Yes it doesn’t compile on WebGL1 in the fragment shader. In our case it was meant for a complex vegetation effect, we are most likely targeting Webgl2 devices for that.

image