【问题标题】:read and write to different textures within same framebuffer读取和写入同一帧缓冲区中的不同纹理
【发布时间】:2020-09-16 04:40:56
【问题描述】:

是否可以从使用相同 FBO 的纹理 A 中读取并写入纹理 B?我不相信这里已经涵盖了这一点。有人问了关于OpenGL 的问题,但没有问WebGL。根据对post 的回答,似乎答案是否定的,但如果有人可以明确地告诉我是否可以在同一个 fbo 中读取/写入纹理,那真的对我有帮助。在我的代码中,我收到以下错误:

WebGL 警告:drawElementsInstanced:纹理级别 0 将由 TEXTURE_2D 单元 0 读取,但由帧缓冲区附件 COLOR_ATTACHMENT1 写入,这将是非法反馈。

以下 sn-p 生成“非法反馈”警告。我知道代码可能看起来/没有用,但关键是完成读/写。总结一下我期望发生的事情是纹理A(或称其为纹理1)应该用于渲染到纹理B(又名纹理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>

【问题讨论】:

    标签: textures framebuffer texture2d render-to-texture webgl2


    【解决方案1】:

    是否可以从纹理 A 读取并写入纹理 B,两者都使用相同的 FBO

    不,这是不可能的。你为什么要这样做?

    你想完成什么?有时写给A,有时写给B?如果是这样,请制作 2 个 fbo。 fboA 和 fboB,将纹理 A 附加到 fboA,将 B 附加到 fboB。当您想写入 A 时绑定 fboA。当你想写 B 时绑定 fboB。如果你有时想同时写 fboAB 并附上 A 和 B。

    虽然代码没有设置为在您同时附加两个纹理时写入 B。见:WebGL 2.0: Multiple output textures from the same program

    更新

    代码将texture[0]texture[1] 都附加到帧缓冲区。所以此时texture[0]texture[1] 都已设置为写入。同样在这一点上,texture[1] 被绑定到纹理单元 0 以供读取。

    着色器仅引用一个纹理,即使它声明了 u_image0u_image1,但您从不使用 u_image1

    然后代码将texture[1] 分配给纹理单元1,但它从未设置u_image0,因此u_image0 使用纹理单元0,因为制服默认为0。

    然后代码调用gl.drawArrays。因为帧缓冲区同时使用texture[0]texture[1] 进行写入,并且因为着色器使用纹理单元0 上的texture[1] 进行读取,这是一个反馈循环,因此代码会出错。

    这是一个可以粘贴到 sn-p HTML 顶部的脚本

    <script src="https://greggman.github.io/webgl-helpers/webgl-check-framebuffer-feedback.js"></script>
    

    来自https://greggman.github.io/webgl-helpers/

    当我这样做并检查 JavaScript 控制台时,我看到了这个错误

    未捕获的错误:WebGL 反馈循环:统一纹理:绑定到纹理单元 0 的 u_image0 也附加到附件上的当前帧缓冲区: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,而不是写入 A 并写入 B。我想要这样做的原因是获取纹理 A 并使用一些生成纹理 B 的值。你的回答是说读 A 写 B 是不可能的(非法反馈)?
    • 完美。这就是我所缺少的......所以我不将两者都附加到帧缓冲区(因为这是用于写作,明白了)。我将纹理绑定到纹理单元以供阅读。完美的。再次感谢 gman。
    猜你喜欢
    • 1970-01-01
    • 2023-03-17
    • 2014-12-05
    • 2011-10-09
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多