Index expression must be constant - WebGL/GLSL error

15,486

Solution 1

As background -- GLSL looks a lot like C, but compiles a bit different. Things are very unrolled, and conditionals may be executed in parallel and switched at the end, that sort of thing. Depends on the hardware...

You can use loop indices or constants to index into arrays. The assignment in your loop is ok, but the access by tileID isn't.

WebGL Shader language is from GLES, documented

http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf

The Appendix, section 5, discusses:

Indexing of Arrays, Vectors and Matrices
Definition:
constant-index-expressions are a superset of constant-expressions. Constant-index-expressions can include loop indices as defined in Appendix A section 4.
The following are constant-index-expressions:
• Constant expressions
• Loop indices as defined in section 4
• Expressions composed of both of the above
When used as an index, a constant-index-expression must have integral type.

Hope that helps!


Oh, as for fixing it, in the exact example above... looks like you could compute from tileID rather than precompute and index.

Or, precompute whatever array you like, and pass it in as a texture. A texture, of course, can be indexed however you like.

Here's a javascript helper method I use, to pass floats down to the shaders:

function glSetupStuff() { ...
...
if(!gl.getExtension("OES_texture_float"))   // <<-- enables RGBA float values, handy!
    alert("cant pass in floats, use 8-bit values instead.");
... }

/*
 * Pass in an array of rgba floats,
 * for example: var data = new Float32Array([0.1,0.2,0.3,1,  .5,.5,1.0,1]);
 */
function textureFromFloats(gl,width,height,float32Array) 
{
var oldActive = gl.getParameter(gl.ACTIVE_TEXTURE);
gl.activeTexture(gl.TEXTURE15); // working register 31, thanks.

var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 
                width, height, 0, 
                gl.RGBA, gl.FLOAT, float32Array);

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.bindTexture(gl.TEXTURE_2D, null);

gl.activeTexture(oldActive);

return texture;
}

Note the use of gl.NEAREST, so it doesn't "blur" your values! Then you can set it up before the gl.drawXxx call, with something like

textureUnit = 3;  // from 0 to 15 is ok
gl.activeTexture(gl.TEXTURE0 + textureUnit);
gl.bindTexture(gl.TEXTURE_2D, texture);

var z = gl.getUniformLocation(prog, "uSampler");
gl.uniform1i(z, textureUnit);

And in the shader (I believe fragment or vertex; some earlier webgl's didn't support vertex textures...)

uniform sampler2D uSampler;
...
vec4 value = texture2D(uSampler, vec2(xValueBetween0And1,yValueBetween0And1));

So, you have to index appropriately for the array-as-texture size, within range of 0 to 1. Try to sample from the middle of each value/pixel. Like, if the array is 2 values wide, index by 0.25 and 0.75.

That's the gist of it!

Solution 2

Tested in Safari 9.1.2 on OS X 10.11.6

uniform float data[32];

float getData(int id) {
    for (int i=0; i<32; i++) {
        if (i == id) return data[i];
    }
}

void main(void) {
    float f = getData(yourVariable);
}

Solution 3

I hit this error because I was attempting to use an integer variable to take the nth texture from an array of textures:

// this doesn't compile!

varying vec2 vUv; // uv coords
varying float vTexture; // texture index in array of textures

uniform sampler2D textures[3]; // identify that there are 3 textures

void main() {
  int textureIndex = int(floor(vTexture));
  gl_FragColor = texture2D(textures[textureIndex], vUv);
}

The solution was to break out the texture indexing into a sequence of conditionals:

// this compiles!

varying vec2 vUv; // uv coords
varying float vTexture; // texture index in array of textures

uniform sampler2D textures[3]; // identify that there are 3 textures

void main() {
  int textureIndex = int(floor(vTexture));
  if (textureIndex == 0) {
    gl_FragColor = texture2D(textures[0], vUv);
  } else if (textureIndex == 1) {
    gl_FragColor = texture2D(textures[1], vUv);
  } else if (textureIndex == 2) {
    gl_FragColor = texture2D(textures[2], vUv);
  } 
}
Share:
15,486

Related videos on Youtube

Joey Morani
Author by

Joey Morani

Updated on July 08, 2022

Comments

  • Joey Morani
    Joey Morani almost 2 years

    I'm having trouble accessing an array in a fragment shader using a non-constant int as the index. I've removed the formula as it wouldn't make much sense here anyway, but my code is meant to calculate the tileID based on the current pixel and use that to determine the color.

    Here's my code:

    int tileID = <Insert formula here>;
    
    vec3 colorTest;
    
    int arrayTest[1024];
    for (int x = 0; x < 1024; x++) {
        if (x == 1) arrayTest[x] = 1;
        else arrayTest[x] = 2;
    }
    
    if (arrayTest[tileID] == 1) colorTest = vec3(0.0, 1.0, 0.0);
    else if (arrayTest[tileID] == 2) colorTest = vec3(1.0, 0.0, 0.0);
    else colorTest = vec3(0.0, 0.0, 0.0);
    

    Apparently GLSL doesn't like this and I get the error:

    '[]' : Index expression must be constant

    Does anyone know how I would fix this? Thanks.

  • david van brink
    david van brink over 10 years
    Ah, looks to be same question here, stackoverflow.com/questions/6247572/…
  • Joey Morani
    Joey Morani over 10 years
    Thanks David. I think passing the data in as a texture might best. Could you explain how I would do that, or point me in the right direction? I assumed textures were only for images. Thanks again.
  • david van brink
    david van brink over 10 years
    Added some code to show how to do this... Yes, textures are generally for images, but if you turn off the interpolation, your shader can reliably access any "pixel" in it, and what you put in the R, G, B, and A slots is up to you. You can pass in floating point values on most hardware, so there's a lot of flexibility there!
  • Andon M. Coleman
    Andon M. Coleman over 10 years
    @Joey.Morani: Also, note that the type of array indexing allowed varies by resource type and shader stage. For instance, in a vertex shader in OpenGL ES 2.0, you can index a uniform array using a non-const integer expression. In a fragment shader, it must be const-index (rather, it must support this - implementations are free to go above and beyond this requirement, however). This is explained in depth in the official GLSL specification <pp. 109-110 - A.5>, it might be worth reading...
  • Joey Morani
    Joey Morani over 10 years
    Thanks Andon! Didn't know this. Yet another way to solve the problem I was having. :)
  • Tomáš Zato
    Tomáš Zato over 8 years
    Pixi.js anyone? :( I need to solve this problem but all solutions use raw webGL.
  • bradleygriffith
    bradleygriffith over 8 years
    This was helpful and correct about indexing but Im curious if anyone has any advice for avoiding dependent texture reads (stackoverflow.com/a/27028431/827441) when using this implementation. Seems like pulling values out of this data texture would constitute a dependent texture read in any case where the 'x/y position' of your data wasnt exactly the same as your fragment's.
  • david van brink
    david van brink over 8 years
    It's true, yes... using a texture to pass in data like this is exactly a dependent texture read. It is (should be?) same performance footprint as any other way of passing in an array of data. If the texture is read in the vertex shader then it's not bad at all. // As mentioned in the SO question you cite, nowadays dependent texture reads aren't very evil.
  • Sturm
    Sturm about 5 years
    On Chrome: 'i' : Loop index cannot be compared with non-constant expression
  • wilddev
    wilddev over 3 years
    Can anybody tell something about the performance of this solution?
  • duhaime
    duhaime over 3 years
    @wilddev To expedite this one could of course change the conditional sequence into a binary search algorithm. I don't know of any faster way but if you come across another potential approach I'm happy to investigate!