Индексное выражение должно быть постоянным - ошибка WebGL/GLSL

У меня возникли проблемы с доступом к массиву в шейдере фрагментов с использованием не константного int в качестве индекса. Я удалил формулу, так как в любом случае это не имеет особого смысла, но мой код предназначен для вычисления 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 не нравится, и я получаю сообщение об ошибке:

'[]': Индексное выражение должно быть постоянным

Кто-нибудь знает, как я это исправим? Спасибо.

Ответ 1

В качестве фона - 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 с чем-то вроде

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.

В этом суть!

Ответ 2

Протестировано в Safari 9.1.2 на 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);
}