指定帧缓冲区并绑定统一纹理的正确顺序?

时间:2018-07-16 18:04:38

标签: webgl

是否存在设置程序输出并将纹理绑定到片段着色器中的制服的顺序?

我有以下代码。如果将包含“ attachFrameBuffer”的行放在最后一个“ g.uniform1i()”调用之后,则会出现错误:

There is no texture bound to the unit 1.

但是,如果我把它们留在原处,那一切都很好。令我担心的是,我可能错过了更多的初始化操作。

gl.useProgram(program);

// Create and bind a framebuffer
var outputTexture = this.makeTexture(gl.FLOAT, null);
this.attachFrameBuffer(outputTexture);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, aTexture);
gl.uniform1i(AHandle, 0);

gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, bTexture);
gl.uniform1i(BHandle, 1);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

和makeTexture的代码:

   texture = gl.createTexture();
   // Bind the texture so the following methods effect this texture.
   gl.bindTexture(gl.TEXTURE_2D, texture);
   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
   // Pixel format and data for the texture
   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, type, data);
   // Unbind the texture.
   gl.bindTexture(gl.TEXTURE_2D, null);

attachFrameBuffer()的代码:

   frameBuffer = gl.createFramebuffer();
   gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
   gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); 

1 个答案:

答案 0 :(得分:3)

纹理绑定到“纹理单位”。纹理单位是全局状态。你可以想象他们像这样

glState = {
  activeTextureUnit: 0,
  textureUnits: [
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    ...
    ... up to gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) ....
  ]
};

当您致电gl.activeTexture(textureUnit)时,WebGL内部发生的事情实际上是有效的

gl.activeTexture = function(textureUnit) {
  // convert texture unit to 0 to N index
  glState.activeTextureUnit = textureUnit - gl.TEXTURE0;
};

实际上,当您致电gl.bindTexture时会发生什么

gl.bindTexture = function(target, texture) {
  glState.textureUnits[glState.activeTextureUnit][target] = texture;
};

统一采样器间接引用纹理单位。您可以给它们指定要从中获取纹理的纹理单位的索引。

因此,在您的情况下,此代码

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, aTexture);

gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, bTexture);

有效地使glState

glState = {
  activeTextureUnit: 1,    // because the last call to activeTexture was gl.TEXTURE1
  textureUnits: [
    { TEXTURE_2D: aTexture, TEXTURE_CUBE_MAP: null, },  // <=- aTexture bound
    { TEXTURE_2D: bTexture, TEXTURE_CUBE_MAP: null, },  // <=- bTexture bound
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    { TEXTURE_2D: null, TEXTURE_CUBE_MAP: null, },
    ...
    ... up to gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) ....
  ]
};

如果您致电

// Create and bind a framebuffer
var outputTexture = this.makeTexture(gl.FLOAT, null);
this.attachFrameBuffer(outputTexture);

然后,makeTexture将不同的纹理绑定到单元1(因为对activeTexture的最后一次调用将activeTextureUnit设置为1。然后最后,它绑定了{{1} },因此不再有纹理绑定到单元1,然后绘制并得到看到的错误

null

没有“正确的顺序”。只有全局的webgl状态,您有责任在调用There is no texture bound to the unit 1. 之前确保正确设置了该状态。您可以按照自己想要的任何方式进行操作。例如,可以让makeTexture在制作纹理时使用其他纹理单元。您还可以让makeTexture查找当前的绑定纹理,使其成为新纹理,然后重新绑定旧纹理。或者,就像您发现的那样,可以在绑定用于绘制的纹理之前调用它。

也就是说,您的代码确实有些混乱,因为大多数WebGL应用程序绘制多次,因此它们通常将资源创建代码(初始化)与渲染代码(绘制)分开。创建代码创建着色器,程序,缓冲区,纹理,甚至顶点数组对象,渲染代码使用它们。

然后,渲染代码将设置绘制所需的所有状态

gl.draw???

但是您发布的代码for each thing to draw useProgram bind buffers and set attributes (or use vertex array object) bind textures to texture units set uniforms for program draw 跟在我的useProgram之后,这是创建时间(您不可能在每次绘制调用之前都创建纹理)。因此,随着程序变大,您可能会在初始化/创建而不是绘制/渲染时调用makeTexture