简体   繁体   中英

Very Weird Situation With Framebuffers And Uniforms (Unexpected Feedback)

I have this situation with framebuffers which I don't understand at all. Have a look at the following:

 class Shader { constructor(cvs, dim) { cvs.width = dim[0]; cvs.height = dim[1]; this.gl = twgl.getContext(cvs); this.bfi = twgl.createBufferInfoFromArrays(this.gl, { a_position: { numComponents: 2, data: [-1, -1, -1, 3, 3, -1] } }); this.pgi = { write: twgl.createProgramInfo(this.gl, ["vs", "fs_write"]), read: twgl.createProgramInfo(this.gl, ["vs", "fs_read"]) }; this.fbs = Array(2).fill().map(() => twgl.createFramebufferInfo(this.gl)); this.gl.useProgram(this.pgi.write.program); twgl.setUniforms(this.pgi.write, { u_resolution: dim }); this.gl.useProgram(this.pgi.read.program); twgl.setUniforms(this.pgi.read, { u_framebuffer: this.fbs[0].attachments[0], u_resolution: dim }); } ldImg(src, handler) { this.gl.useProgram(this.pgi.write.program); twgl.setUniforms(this.pgi.write, { u_texture: twgl.createTexture( this.gl, { src }, handler ) }); } frame() { twgl.bindFramebufferInfo(this.gl, this.fbs[0]); this.gl.useProgram(this.pgi.write.program); twgl.setBuffersAndAttributes(this.gl, this.pgi.write, this.bfi); twgl.drawBufferInfo(this.gl, this.bfi); twgl.bindFramebufferInfo(this.gl, null); this.gl.useProgram(this.pgi.read.program); // twgl.setUniforms(this.pgi.read, { // u_framebuffer: this.fbs[0].attachments[0] // }); twgl.setBuffersAndAttributes(this.gl, this.pgi.read, this.bfi); twgl.drawBufferInfo(this.gl, this.bfi); // this.fbs = [this.fbs[1], this.fbs[0]]; } } (function main() { const dim = [512, 512]; const shr = new Shader(document.querySelector("canvas"), dim); shr.ldImg("https://assets.codepen.io/854924/ad.png", frame); function frame(ms) { shr.frame(); window.requestAnimationFrame(frame); } })();
 <script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script> <script id="vs" type="x-shader/x-vertex"> attribute vec4 a_position; void main() { gl_Position = a_position; } </script> <script id="fs_write" type="x-shader/x-fragment"> precision highp float; uniform vec2 u_resolution; uniform sampler2D u_texture; void main() { gl_FragColor = texture2D(u_texture, (gl_FragCoord.xy + 1.0) / u_resolution); } </script> <script id="fs_read" type="x-shader/x-fragment"> precision highp float; uniform sampler2D u_framebuffer; uniform vec2 u_resolution; void main() { gl_FragColor = vec4( 1.0 - texture2D(u_framebuffer, gl_FragCoord.xy / u_resolution).rgb, 1.0 ); } </script> <canvas></canvas>

I tried to keep things as simple as possible. There are two programs, write and read , where write reads from a texture, translates by one pixel and writes to the output. Then read reads from a framebuffer, inverts the image and writes to the output. In the current situation, I let write render to the framebuffer and read render to the screen. Everything works as expected.

Now if I uncomment the first commented line in the JS, I get the error Feedback loop formed between Framebuffer and active Texture , which is weird, as the uniform u_framebuffer in read is always set to the same framebuffer, I just rebind a uniform already bound. Also it should create no feedback loop, as write doesn't have the framebuffer bound while read doesn't attempt to render to the framebuffer.

If I uncomment both commented lines in the JS, so that the framebuffer get switched out every frame, I can see the texture 'walk' across the screen. So that is clearly a feedback loop (the one pixel offset getting accumulated), but I can't see where it is coming from. write just reads from the texture, which should be absolutely static, but somehow it seems to consume its own output.

I'm quite lost here, either there is some logic in uniforms and framebuffers which I don't understand at all, or some bug I never encountered before?

Textures are not like uniforms, textures are bound to one of the globally available texture units which is a stateful operation, aka once you bind a texture to a unit it remains bound until you unbind it, just like framebuffers. So when you call setUniforms with a texture, it binds the texture to a unit and tells the sampler which of the global units to read from, omitting this results in the following shaders reading from whatever was bound previously, in your case the framebuffer texture. So what you want to do is rebind the read texture before you render with your read program:

    this.gl.useProgram(this.pgi.write.program);
    twgl.setUniforms(this.pgi.write, {
       u_texture: this.readTexture //@TODO: store read texture in ldImg
    });
    twgl.setBuffersAndAttributes(this.gl, this.pgi.write, this.bfi);
    twgl.drawBufferInfo(this.gl, this.bfi);
    twgl.bindFramebufferInfo(this.gl, null);
    this.gl.useProgram(this.pgi.read.program);
    twgl.setUniforms(this.pgi.read, {
       u_framebuffer: this.fbs[0].attachments[0]
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM