是否可以在着色器中访问预设的纹理单元?

Is it possible to access pre-set textureunits within a shader?

TL;DR: 我已经激活纹理并将其绑定到纹理单元 1-4,现在我希望在着色器中使用它们而不使用程序特定的统一位置。是否可以使用“全局”纹理单元从纹理中读取数据?

我很难理解如何在 webgl 中访问纹理。在 this tutorial on textures 中,它说“纹理单元是纹理引用的全局数组。”,这听起来不错。我使用

绑定了我的纹理
gl.activeTexture(gl.TEXTURE1 + i);
gl.bindTexture(gl.TEXTURE_2D, tex);

其中 i 是新创建的纹理的索引,tex 是纹理本身。

但它没有给出如何使用此全局引用访问着色器中的纹理的示例。在 this tutorial from the same page it does give an example of how to access the textures from within a shader, but only when using an uniform position (much like how an attribute is accessed), which requires a program (and thus, removing the global aspect, in a way, since it's dependent on the program to be accessed). In this preceding tutorial 中,完全跳过了有关如何实际设置纹理“u_texture”的任何细节。

简而言之,我有两个程序,如果可能的话,我希望在这两个程序中全局使用纹理,而不必在每个程序上都使用 getUniformLocation。看到纹理单元的承诺,我以为我可以使用它们,但我不知道如何实际做类似

的事情
sampler2D textureUnit2;

在着色器中。这可能吗?我是否将 textureUnits 的实现与使用它们的实用性混淆了?

非常感谢!

I wish to use textures globally in both of them without having to getUniformLocation on each program, if it's possible to do so

不可能这样做。

纹理单元是全局的。你可以把它想象成 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制服设置为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);

This diagram 可能有助于可视化其工作原理

上面你可以看到着色器程序(金色)有两个制服decaldiffuse

  • decal 设置为 3,因此它引用纹理单元 3。纹理单元 3 绑定到 decalTexture(F 的图像)

  • diffuse 设置为 6,因此它引用纹理单元 6。纹理单元 3 绑定到 diffuseTexture(4x4 棋盘)

请注意,纹理单元 0 也绑定到 decalTexture,但着色器程序未引用纹理单元 0,因此至少出于绘制像素的目的,换句话说,执行着色器程序、纹理单元0 未被使用。