索引表达式必须是常量 - WebGL/GLSL 错误

18

我在片段着色器中使用非常量整数作为索引访问数组时遇到了困难。这里我已经删除了公式,因为即使在这里也不会有太多意义,但我的代码旨在根据当前像素计算tileID并使用它来确定颜色。

这是我的代码:

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);

显然GLSL不喜欢这样,我会得到错误:

'[]':索引表达式必须是常数

有人知道我该如何修复这个错误吗?谢谢。

3个回答

15
作为背景资料,GLSL看起来很像C语言,但编译方式有点不同。一些东西会被展开,条件语句可能会并行执行并在最后进行切换等。这取决于硬件...。
可以使用循环索引或常量索引数组。您循环中的赋值是可以的,但tileID的访问不是。
WebGL着色器语言来源于GLES,并有文档可供参考。 http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf 附录第5节讨论了此事。
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.

希望这有所帮助!


哦,至于修复它,在上面的确切示例中...看起来你可以从tileID计算而不是预先计算和索引。

或者,预先计算任何你喜欢的数组,并将其作为纹理传递进去。当然,纹理可以按照你喜欢的方式进行索引。

这是我使用的一个javascript辅助方法,将浮点数传递到着色器中:

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;
}

注意使用gl.NEAREST,这样就不会使您的值“模糊”!然后,您可以在gl.drawXxx调用之前设置它,例如:

注意使用gl.NEAREST,这样就不会使您的值“模糊”!然后,您可以在gl.drawXxx调用之前设置它,例如

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);

在着色器中(我相信是片元着色器或顶点着色器;一些早期的WebGL版本不支持顶点纹理...)

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

因此,您必须适当地为数组作为纹理的尺寸进行索引,范围为0到1之间。尝试从每个值/像素的中间进行采样。例如,如果数组宽度为2个值,则通过索引0.25和0.75。

这就是要点!


啊,这里似乎有同样的问题,https://dev59.com/fG025IYBdhLWcg3wCxVN - david van brink
谢谢David。我认为将数据作为纹理传递可能是最好的选择。你能解释一下我该如何做,或者指点我正确的方向吗?我原以为纹理只用于图像。再次感谢。 - Joey Morani
添加了一些代码以展示如何实现这个... 是的,纹理通常用于图像,但如果关闭插值,您的着色器可以可靠地访问其中的任何“像素”,而您放入R、G、B和A槽中的内容取决于您。在大多数硬件上,您可以传递浮点值,因此有很大的灵活性! - david van brink
4
请注意,允许的数组索引类型因资源类型和着色器阶段而异。例如,在OpenGL ES 2.0中的顶点着色器中,您可以使用非常量整数表达式索引一个uniform数组。在片段着色器中,必须使用常量索引(或者说必须支持此功能——然而,实现可以超出此要求)。这在官方的GLSL规范[http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf] <pp.109-110 - A.5>中有详细说明,值得一读。 - Andon M. Coleman
谢谢Andon!我不知道这个。这是解决我遇到的问题的另一种方法。 :) - Joey Morani
显示剩余3条评论

6

在OS X 10.11.6上的Safari 9.1.2中进行了测试

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);
}

1
在Chrome上:'i':循环索引不能与非常量表达式进行比较 - Sturm

4

我遇到了这个错误,因为我试图使用一个整数变量从纹理数组中取出第n个纹理:

// 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);
}

解决方案是将纹理索引分解为一系列条件语句:
// 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);
  } 
}

有人能说说这个解决方案的性能吗? - wilddev
@wilddev 为了加快这个过程,当然可以将条件序列改为二分搜索算法。我不知道还有什么更快的方法,但如果你遇到另一种可能的方法,我很乐意调查! - duhaime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接