在同一个帧缓冲区中读取和写入不同的纹理
read and write to different textures within same framebuffer
是否可以从纹理 A 读取和写入纹理 B,两者都使用相同的 FBO?我不相信这已经被覆盖在这里。有人问了关于 OpenGL, but not about WebGL. It seems like the answer is no, based on the answer to this 的问题,但如果有人能明确地告诉我是否 reading/writing 到同一个 fbo 中的纹理是可能的,那真的对我有帮助。在我的代码中出现以下错误:
WebGL 警告:drawElementsInstanced:纹理级别 0 将由 TEXTURE_2D 单元 0 读取,但由帧缓冲区附件 COLOR_ATTACHMENT1 写入,这将是非法反馈。
以下代码段会生成 'illegal feedback' 警告。我知道代码可能 look/be 没有用,但重点是完成 read/write。总结一下我期望发生的事情是 textureA(或称它为 texture 1)应该用于渲染到 texture B(又名 texture 2)。然后我应该在屏幕上看到一些东西;很可能是 4 个贪婪方块:
/* YOU CAN PROBABLY IGNORE THIS AS IT'S INITIALIZATION CODE */
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
var fb;
var textures = [];
var tex_datei1 = new Array( 4*64*64 ).fill(9);
var vbo = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
var vertices = new Array( 4*64*64 ).fill( 0 );
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
gl.bufferData( gl.ARRAY_BUFFER , new Float32Array( vertices ) , gl.STATIC_DRAW );
var ibo = gl.createBuffer();
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER , ibo );
var indices = [ ...Array( vertices.length/4 ).keys() ];
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 2;
indices[4] = 3;
indices[5] = 1;
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
var vc = `#version 300 es
precision mediump float;
uniform sampler2D u_image0;
uniform sampler2D u_image1;
in vec3 coordinates;
void main(void) {
float x = coordinates.x;
float y = coordinates.y;
vec4 sample_A = texture( u_image0 , vec2( x , y ) );
sample_A.x *= 64.0 * 4.0;
sample_A.y *= 64.0 * 4.0;
gl_Position = vec4( sample_A.x/10.0 , sample_A.y/10.0 , 0.0 , 1.0 );
gl_PointSize = 30.0;
}
`;
var vs = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource( vs , vc );
gl.compileShader(vs);
if( !gl.getShaderParameter( vs , gl.COMPILE_STATUS ) ){
console.log( gl.COMPILE_STATUS );
console.log( gl.getShaderInfoLog(vs) );
}
var fc = `#version 300 es
precision highp float;
out vec4 color;
void main(void) {
color = vec4( 0.5 , 0.5 , 0.0 , 1.0 );
}
`;
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, fc);
gl.compileShader(fs);
if( !gl.getShaderParameter( fs , gl.COMPILE_STATUS ) ){
console.log( gl.COMPILE_STATUS );
console.log( gl.getShaderInfoLog(fs) );
}
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram( program);
var coordinates_u_loc = gl.getAttribLocation(program, "coordinates");
var u_image0_loc = gl.getUniformLocation( program , "u_image0");
var die_berechnung_u_image1Location = gl.getUniformLocation( program , "u_image1");
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER , ibo );
gl.enableVertexAttribArray( coordinates_u_loc );
gl.vertexAttribPointer( coordinates_u_loc , 4 , gl.FLOAT , false , 0 , 0 );
/* HERE IS WHERE I GET INTO FRAMEBUFFER SETUP */
fb = gl.createFramebuffer();
gl.bindFramebuffer( gl.FRAMEBUFFER , fb );
gl.uniform1i( u_image0_loc , 0);
//gl.uniform1i( u_image1_loc , 1);
var internal_formats = [ gl.RGBA , gl.RGBA ];
var formats = [ gl.RGBA , gl.RGBA ];
var types = [ gl.UNSIGNED_BYTE , gl.UNSIGNED_BYTE ];
var datas = [ new Uint8Array( tex_datei1.slice() ) , new Uint8Array( tex_datei1.slice() ) ];
for( var i = 0 ; i < datas.length ; i++ ){
var texture = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D , texture );
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
gl.texImage2D( gl.TEXTURE_2D ,
0 ,
internal_formats[i] ,
64 ,
64 ,
0 ,
formats[ i ] ,
types[ i ] ,
datas[ i ]
);
textures.push( texture );
}
/* HERE MAY LIE MY ISSUE AS I HAVE NO IDEA HOW TO SPECIFY THAT THE SECOND TEXTURE SHOULD BE WRITTEN TO. I'VE TRIED MESSING AROUND WITH THE LAYERS PARAMETER BUT THAT JUST CREATES MIPS ISSUES. */
gl.bindTexture( gl.TEXTURE_2D , textures[ 0 ] );
var attachmentPoint = gl.COLOR_ATTACHMENT0;
gl.framebufferTexture2D( gl.FRAMEBUFFER , attachmentPoint , gl.TEXTURE_2D , textures[ 0 ] , 0 );
gl.bindTexture( gl.TEXTURE_2D , textures[ 1 ] );
var attachmentPoint = gl.COLOR_ATTACHMENT1;
gl.framebufferTexture2D( gl.FRAMEBUFFER , attachmentPoint , gl.TEXTURE_2D , textures[ 1 ] , 0 );
var er = gl.checkFramebufferStatus( gl.FRAMEBUFFER );
if( er !== 36053 ){
console.log( er );
}
/* DRAW */
gl.clearColor( 0.0 , 0.0 , 0.0 , 0.0 );
gl.enable( gl.DEPTH_TEST );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.viewport( 0,0, 64 , 64 );
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[1] );
gl.drawElements( gl.POINTS , indices.length , gl.UNSIGNED_SHORT , 0 );
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<body>
<canvas width="64" heigh="64" style="width:64px; height:64px; border:thick solid #0000FF; "></canvas>
</body>
</html>
Is it possible to read from texture A and write to texture B that both use the same FBO
不,这是不可能的。为什么你会想要那样做?
你想完成什么?有时写给A,有时写给B?如果是这样,请制作 2 个 fbos。 fboA 和 fboB,将纹理 A 附加到 fboA,将 B 附加到 fboB。当你想写入 A 时绑定 fboA。当你想写入 B 时绑定 fboB。如果您有时想同时写入 make fboAB 并同时附加 A 和 B。
虽然代码没有设置为在您附加了两个纹理时写入 B。参见:
更新
代码将 texture[0]
和 texture[1]
都附加到帧缓冲区。因此,此时 texture[0]
和 texture[1]
都已设置好写入。同样在那时 texture[1]
绑定到纹理单元 0 以供读取。
着色器只引用一个纹理,即使它同时声明了 u_image0
和 u_image1
你永远不会使用 u_image1
.
代码然后将 texture[1]
分配给纹理单元 1,但它从未设置 u_image0
因此 u_image0
使用纹理单元 0,因为制服默认为 0。
然后代码调用 gl.drawArrays
。因为帧缓冲区同时使用 texture[0]
和 texture[1]
进行写入,并且因为着色器在纹理单元 0 上使用 texture[1]
进行读取,这是一个反馈循环,所以代码会出错。
这是您可以粘贴到代码段顶部的脚本 HTML
<script src="https://greggman.github.io/webgl-helpers/webgl-check-framebuffer-feedback.js"></script>
来自 https://greggman.github.io/webgl-helpers/
当我这样做并检查 JavaScript 控制台时,我看到了这个错误
Uncaught Error: WebGL feedback loop: texture on uniform: u_image0 bound to texture unit 0 is also attached to current framebuffer on attachment: COLOR_ATTACHMENT1
如果你想使用纹理 A 生成纹理 B,那么在初始化时你将 B 附加到帧缓冲区对象,并且只附加 B
// at init time
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, textureB, 0);
然后在渲染时绑定该帧缓冲区
// make gl.drawXXX write to textureB
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
将纹理 A 绑定到某个纹理单元。
const unit = 7;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE, textureA);
告诉着色器采样器您将纹理放在哪个单元上
gl.uniform1i(u_image0Location, unit);
更多信息here
是否可以从纹理 A 读取和写入纹理 B,两者都使用相同的 FBO?我不相信这已经被覆盖在这里。有人问了关于 OpenGL, but not about WebGL. It seems like the answer is no, based on the answer to this
WebGL 警告:drawElementsInstanced:纹理级别 0 将由 TEXTURE_2D 单元 0 读取,但由帧缓冲区附件 COLOR_ATTACHMENT1 写入,这将是非法反馈。
以下代码段会生成 'illegal feedback' 警告。我知道代码可能 look/be 没有用,但重点是完成 read/write。总结一下我期望发生的事情是 textureA(或称它为 texture 1)应该用于渲染到 texture B(又名 texture 2)。然后我应该在屏幕上看到一些东西;很可能是 4 个贪婪方块:
/* YOU CAN PROBABLY IGNORE THIS AS IT'S INITIALIZATION CODE */
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
var fb;
var textures = [];
var tex_datei1 = new Array( 4*64*64 ).fill(9);
var vbo = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
var vertices = new Array( 4*64*64 ).fill( 0 );
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
gl.bufferData( gl.ARRAY_BUFFER , new Float32Array( vertices ) , gl.STATIC_DRAW );
var ibo = gl.createBuffer();
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER , ibo );
var indices = [ ...Array( vertices.length/4 ).keys() ];
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 2;
indices[4] = 3;
indices[5] = 1;
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW);
var vc = `#version 300 es
precision mediump float;
uniform sampler2D u_image0;
uniform sampler2D u_image1;
in vec3 coordinates;
void main(void) {
float x = coordinates.x;
float y = coordinates.y;
vec4 sample_A = texture( u_image0 , vec2( x , y ) );
sample_A.x *= 64.0 * 4.0;
sample_A.y *= 64.0 * 4.0;
gl_Position = vec4( sample_A.x/10.0 , sample_A.y/10.0 , 0.0 , 1.0 );
gl_PointSize = 30.0;
}
`;
var vs = gl.createShader( gl.VERTEX_SHADER );
gl.shaderSource( vs , vc );
gl.compileShader(vs);
if( !gl.getShaderParameter( vs , gl.COMPILE_STATUS ) ){
console.log( gl.COMPILE_STATUS );
console.log( gl.getShaderInfoLog(vs) );
}
var fc = `#version 300 es
precision highp float;
out vec4 color;
void main(void) {
color = vec4( 0.5 , 0.5 , 0.0 , 1.0 );
}
`;
var fs = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fs, fc);
gl.compileShader(fs);
if( !gl.getShaderParameter( fs , gl.COMPILE_STATUS ) ){
console.log( gl.COMPILE_STATUS );
console.log( gl.getShaderInfoLog(fs) );
}
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
gl.useProgram( program);
var coordinates_u_loc = gl.getAttribLocation(program, "coordinates");
var u_image0_loc = gl.getUniformLocation( program , "u_image0");
var die_berechnung_u_image1Location = gl.getUniformLocation( program , "u_image1");
gl.bindBuffer( gl.ARRAY_BUFFER , vbo );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER , ibo );
gl.enableVertexAttribArray( coordinates_u_loc );
gl.vertexAttribPointer( coordinates_u_loc , 4 , gl.FLOAT , false , 0 , 0 );
/* HERE IS WHERE I GET INTO FRAMEBUFFER SETUP */
fb = gl.createFramebuffer();
gl.bindFramebuffer( gl.FRAMEBUFFER , fb );
gl.uniform1i( u_image0_loc , 0);
//gl.uniform1i( u_image1_loc , 1);
var internal_formats = [ gl.RGBA , gl.RGBA ];
var formats = [ gl.RGBA , gl.RGBA ];
var types = [ gl.UNSIGNED_BYTE , gl.UNSIGNED_BYTE ];
var datas = [ new Uint8Array( tex_datei1.slice() ) , new Uint8Array( tex_datei1.slice() ) ];
for( var i = 0 ; i < datas.length ; i++ ){
var texture = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D , texture );
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);
gl.texImage2D( gl.TEXTURE_2D ,
0 ,
internal_formats[i] ,
64 ,
64 ,
0 ,
formats[ i ] ,
types[ i ] ,
datas[ i ]
);
textures.push( texture );
}
/* HERE MAY LIE MY ISSUE AS I HAVE NO IDEA HOW TO SPECIFY THAT THE SECOND TEXTURE SHOULD BE WRITTEN TO. I'VE TRIED MESSING AROUND WITH THE LAYERS PARAMETER BUT THAT JUST CREATES MIPS ISSUES. */
gl.bindTexture( gl.TEXTURE_2D , textures[ 0 ] );
var attachmentPoint = gl.COLOR_ATTACHMENT0;
gl.framebufferTexture2D( gl.FRAMEBUFFER , attachmentPoint , gl.TEXTURE_2D , textures[ 0 ] , 0 );
gl.bindTexture( gl.TEXTURE_2D , textures[ 1 ] );
var attachmentPoint = gl.COLOR_ATTACHMENT1;
gl.framebufferTexture2D( gl.FRAMEBUFFER , attachmentPoint , gl.TEXTURE_2D , textures[ 1 ] , 0 );
var er = gl.checkFramebufferStatus( gl.FRAMEBUFFER );
if( er !== 36053 ){
console.log( er );
}
/* DRAW */
gl.clearColor( 0.0 , 0.0 , 0.0 , 0.0 );
gl.enable( gl.DEPTH_TEST );
gl.clear( gl.COLOR_BUFFER_BIT );
gl.viewport( 0,0, 64 , 64 );
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, textures[1] );
gl.drawElements( gl.POINTS , indices.length , gl.UNSIGNED_SHORT , 0 );
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<body>
<canvas width="64" heigh="64" style="width:64px; height:64px; border:thick solid #0000FF; "></canvas>
</body>
</html>
Is it possible to read from texture A and write to texture B that both use the same FBO
不,这是不可能的。为什么你会想要那样做?
你想完成什么?有时写给A,有时写给B?如果是这样,请制作 2 个 fbos。 fboA 和 fboB,将纹理 A 附加到 fboA,将 B 附加到 fboB。当你想写入 A 时绑定 fboA。当你想写入 B 时绑定 fboB。如果您有时想同时写入 make fboAB 并同时附加 A 和 B。
虽然代码没有设置为在您附加了两个纹理时写入 B。参见:
更新
代码将 texture[0]
和 texture[1]
都附加到帧缓冲区。因此,此时 texture[0]
和 texture[1]
都已设置好写入。同样在那时 texture[1]
绑定到纹理单元 0 以供读取。
着色器只引用一个纹理,即使它同时声明了 u_image0
和 u_image1
你永远不会使用 u_image1
.
代码然后将 texture[1]
分配给纹理单元 1,但它从未设置 u_image0
因此 u_image0
使用纹理单元 0,因为制服默认为 0。
然后代码调用 gl.drawArrays
。因为帧缓冲区同时使用 texture[0]
和 texture[1]
进行写入,并且因为着色器在纹理单元 0 上使用 texture[1]
进行读取,这是一个反馈循环,所以代码会出错。
这是您可以粘贴到代码段顶部的脚本 HTML
<script src="https://greggman.github.io/webgl-helpers/webgl-check-framebuffer-feedback.js"></script>
来自 https://greggman.github.io/webgl-helpers/
当我这样做并检查 JavaScript 控制台时,我看到了这个错误
Uncaught Error: WebGL feedback loop: texture on uniform: u_image0 bound to texture unit 0 is also attached to current framebuffer on attachment: COLOR_ATTACHMENT1
如果你想使用纹理 A 生成纹理 B,那么在初始化时你将 B 附加到帧缓冲区对象,并且只附加 B
// at init time
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, textureB, 0);
然后在渲染时绑定该帧缓冲区
// make gl.drawXXX write to textureB
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
将纹理 A 绑定到某个纹理单元。
const unit = 7;
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE, textureA);
告诉着色器采样器您将纹理放在哪个单元上
gl.uniform1i(u_image0Location, unit);
更多信息here