Я буду sh использовать текстуры глобально в обоих из них без необходимости getUniformLocation в каждой программе, если это возможно
Это не это возможно.
Текстурные блоки глобальные. Вы можете думать об этом как о глобальном массиве в JavaScript
const textureUnits = [
{ TEXTURE_2D: someTexture, }, // unit 0
{ TEXTURE_2D: someOtherTexture, }, // unit 1
{ TEXTURE_2D: yetAnotherTexture, }, // unit 2
...
];
. Вы можете привязать текстуру к текстурному модулю, сначала вызвав gl.activeTexture
, а затем gl.bindTexture
. Пример:
const unit = 2;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, yetAnotherTexture);
yetAnotherTexture
теперь привязан к текстурному блоку 2
Отдельно у вас есть шейдерные программы. Чтобы получить доступ к текстуре, вы объявляете uniform sampler2d someName;
в своем шейдере.
uniform sampler2D foobar;
Думайте об этом как об индексе в текстурных модулях. Затем вам нужно установить индекс (указать программе шейдера, какой глобальный блок текстуры использовать)
const foobarLocation = gl.getUniformLocation(someProgram, "foobar");
...
// tell the program to use the texture on unit 2
const indexOfTextureUnit = 2;
gl.uniform1i(foobarLocation, indexOfTextureUnit);
Сами текстурные блоки являются глобальными. Униформа программы уникальна для каждой программы. Вы должны указать им, на какой блок текстуры вы хотите ссылаться. По умолчанию для униформ установлено значение 0, поэтому, если вы хотите, чтобы они ссылались на текстурный блок 0, вам не нужно их устанавливать, но для любого другого текстурного блока вам нужно их установить.
Представьте, что функция GLSL texture2D
была написана в JavaScript. Это будет работать примерно так:
function texture2d(sampler, texcoord) {
const texture = textureUnits[sampler].TEXTURE_2D;
return getColorFromTextureAtTexcoord(texture, texcoord);
}
Итак, если бы я сделал это
uniform sampler2D foobar;
varying vec2 v_texcoord;
void main() {
gl_FragColor = texture2D(foobar, v_texcoord);
}
Тогда я установил бы foobar
uniform на 2
const foobarLocation = gl.getUniformLocation(someProgram, "foobar");
...
// tell the program to use the texture on unit 2
const indexOfTextureUnit = 2;
gl.uniform1i(foobarLocation, indexOfTextureUnit);
Это будет ссылка на глобальный блок текстуры с индексом 2.
Примечание: этот код в вашем вопросе
gl.activeTexture(gl.TEXTURE1 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);
кажется неправильным. Он должен использовать TEXTURE0
в качестве основы, а не TEXTURE1
gl.activeTexture(gl.TEXTURE0 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);
Эта диаграмма может помочь визуализировать, как это работает
введите описание изображения здесь
Выше вы можете увидеть, что программа шейдера (золото) имеет две формы: decal
и diffuse
.
decal
установлено на 3, поэтому ссылка на блок текстуры 3. Блок текстуры 3 привязан к decalTexture
(изображение F).
diffuse
установлен на 6, поэтому он ссылается на блок текстуры 6. Блок текстуры 3 привязан к diffuseTexture
(шахматная доска 4x4)
Обратите внимание, что блок текстуры 0 также привязан к decalTexture
, но программа шейдера не ссылается на блок текстуры 0, поэтому по крайней мере, для отрисовки пикселей, другими словами при выполнении программы шейдера, блок текстуры 0 не используется.