纹理

纹理单元是全局状态。如果它们是用 JavaScript 实现的,那么它们看起来就像这样

// pseudo code
gl = {
  activeTextureUnit: 0,
  textureUnits: [
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
    { TEXTURE_2D: ?, TEXTURE_CUBE_MAP: ? },
  ],
};

你可以选择使用 gl.activeTexture 索引哪个单位。

// pseudo code
gl.activeTexture = function(textureUnit) {
  gl.activeTextureUnit = textureUnit - gl.TEXTURE0;
};

调用 gl.bindTexture 会将纹理绑定到活动纹理单元,就像这样

// pseudo code
gl.bindTexture = function(target, texture) {
  var textureUnit = gl.textureUnits[gl.activeTextureUnit];
  textureUnit[target] = texture;
}

当你有一个使用纹理的着色器程序时,你必须告诉着色器程序你绑定纹理的纹理单元。例如,如果你有这样的着色器

uniform sampler2D diffuse;
uniform sampler2D normalMap;
uniform samplerCube environmentMap;

...

你需要查询统一位置

var diffuseUniformLocation = gl.getUniformLocation(someProgram, "diffuse");
var normalMapUniformLocation = gl.getUniformLocation(someProgram, "normalMap");
var environmmentMapUniformLocation = gl.getUniformLocation(someProgram,
                                                           "environmentMap");

然后,在你的着色器程序成为当前程序之后

gl.useProgram(someProgram);

然后,你需要告诉着色器你执行/将放置纹理的纹理单位。例如

var diffuseTextureUnit = 3;
var normalMapTextureUnit = 5;
var environmentMapTextureUnit = 2;

gl.uniform1i(diffuseUniformLocation, diffuseTextureUnit);
gl.uniform1i(normalMapUniformLocation, normalMapTextureUnit);
gl.uniform1i(environmentMapUniformLocation, environmentMapTextureUnit);

现在你告诉着色器你做/将使用哪些单位。你决定使用哪种纹理单元完全取决于你。

要将纹理实际绑定到纹理单元,你可以执行类似这样的操作

gl.activeTexture(gl.TEXTURE0 + diffuseTextureUnit);
gl.bindTexture(gl.TEXTURE_2D, diffuseTexture);
gl.activeTexture(gl.TEXTURE0 + normalMapTextureUnit);
gl.bindTexture(gl.TEXTURE_2D, normalMapTexture);
gl.activeTexture(gl.TEXTURE0 + environmentMapTextureUnit);
gl.bindTexture(gl.TEXTURE_CUBE_MAP, environmentMapTexture);

对于仅使用 1 个纹理的非常简单的 WebGL 示例,从不调用 gl.activeTexture 是常见的,因为它默认为纹理单元#0。通常不会调用 gl.uniform1i,因为制服默认为 0,因此着色器程序默认情况下会对所有纹理使用纹理单元#0。

所有其他纹理函数也可以处理活动纹理和纹理单元目标。例如,gl.texImage2D 可能看起来像这样

gl.texImage2D = function(target, level, internalFormat, width, height, 
                         border, format, type, data) {
   var textureUnit = gl.textureUnits[gl.activeTextureUnit];
   var texture = textureUnit[target];

   // Now that we've looked up the texture form the activeTextureUnit and
   // the target we can effect a specific texture
   ...
};