指定帧缓冲区和绑定统一纹理的正确顺序?
Right order to specifying framebuffer and binding uniform textures?
是否有在片段着色器中设置程序输出和绑定纹理到制服的顺序?
我有以下代码。如果我在最后一个“g.uniform1i()
”调用之后放置包含“attachFrameBuffer
”的行,我会收到错误消息:
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);
纹理绑定到 "texture units"。纹理单元是全局状态。你可以这样想象他们
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。然后在最后它绑定 null
所以没有纹理绑定到单元 1。然后你绘制并得到你看到的错误
There is no texture bound to the unit 1.
没有"right order"。只有全局 webgl 状态,您有责任确保在调用 gl.draw???
之前正确设置该状态。你可以随心所欲地做到这一点。例如,您可以让 makeTexture 在制作纹理时使用不同的纹理单元。您还可以让 makeTexture 查找当前绑定的纹理,创建新纹理,然后重新绑定旧纹理。或者,就像您发现的那样,您可以在绑定纹理进行绘图之前调用它。
也就是说,您的代码确实看起来有点可疑,因为大多数 WebGL 应用程序绘制了很多次,因此它们通常将资源创建代码(初始化)与渲染代码(绘制)分开。创建代码创建着色器、程序、缓冲区、纹理,也许还有顶点数组对象,渲染代码使用它们。
然后渲染代码将设置绘制所需的所有状态
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
之后,这是创建时间的事情(您不太可能在每次绘制调用之前创建纹理)。因此,随着您的程序变大,您可能会在 init/creation 的其他地方调用 makeTexture
而不是 draw/render 时间
PS:这是一个 webgl state diagram 您可以逐步查看 WebGL 状态变化。
是否有在片段着色器中设置程序输出和绑定纹理到制服的顺序?
我有以下代码。如果我在最后一个“g.uniform1i()
”调用之后放置包含“attachFrameBuffer
”的行,我会收到错误消息:
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);
纹理绑定到 "texture units"。纹理单元是全局状态。你可以这样想象他们
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。然后在最后它绑定 null
所以没有纹理绑定到单元 1。然后你绘制并得到你看到的错误
There is no texture bound to the unit 1.
没有"right order"。只有全局 webgl 状态,您有责任确保在调用 gl.draw???
之前正确设置该状态。你可以随心所欲地做到这一点。例如,您可以让 makeTexture 在制作纹理时使用不同的纹理单元。您还可以让 makeTexture 查找当前绑定的纹理,创建新纹理,然后重新绑定旧纹理。或者,就像您发现的那样,您可以在绑定纹理进行绘图之前调用它。
也就是说,您的代码确实看起来有点可疑,因为大多数 WebGL 应用程序绘制了很多次,因此它们通常将资源创建代码(初始化)与渲染代码(绘制)分开。创建代码创建着色器、程序、缓冲区、纹理,也许还有顶点数组对象,渲染代码使用它们。
然后渲染代码将设置绘制所需的所有状态
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
之后,这是创建时间的事情(您不太可能在每次绘制调用之前创建纹理)。因此,随着您的程序变大,您可能会在 init/creation 的其他地方调用 makeTexture
而不是 draw/render 时间
PS:这是一个 webgl state diagram 您可以逐步查看 WebGL 状态变化。