简体   繁体   English

清除 canvas 并用不同的图像重新渲染 webgl 上下文

[英]Clear canvas and rerender the webgl context with different image

I have a canvas on which I render a given image by applying some WebGL filter on it.我有一个 canvas 我通过在其上应用一些 WebGL 过滤器来渲染给定的图像。 The canvas that is displayed must be reused.显示的 canvas 必须重复使用。 That is I keep getting different images (other part of the program) which I should draw on this canvas with the same filter (fragment shader) being applied.那就是我不断得到不同的图像(程序的其他部分),我应该在这个 canvas 上应用相同的过滤器(片段着色器)。

I created a function drawoncanvas(gl, img, img.width, img.height) here the gl is the webglrenderingcontext of the canvas, img is the html element with image to be used.我创建了一个function drawoncanvas(gl, img, img.width, img.height)这里 gl 是 canvas 的 webglrenderingcontext,img 是 ZFC35FDC70D5FC69D26988 元素的 ZFC35FDC70D5FC69D26988。 The function has all the WebGL processing part. function 拥有所有的 WebGL 处理部分。 So, whenever I get a new image to be processed and display on canvas.所以,每当我得到一个要处理的新图像并在 canvas 上显示时。 I call this function with the new img element and webglrenderingcontext of the same canvas.我用相同的 canvas 的新 img 元素和 webglrenderingcontext 将此称为 function。

The issue I am facing is that I can see the previously painted content on canvas behind the current content (wherever the current content is transparent).我面临的问题是我可以在当前内容的后面看到之前在canvas上绘制的内容(无论当前内容是透明的)。 And if I pass same image twice the canvas shows the content upside down.如果我通过两次相同的图像 canvas 显示内容颠倒。

I want to know how can I clear the canvas and/or the WebGL rendering context before I start using the new image.我想知道在开始使用新图像之前如何清除 canvas 和/或 WebGL 渲染上下文。 So that it doesn't show the the old stuff beneath or give these issues.这样它就不会显示下面的旧内容或给出这些问题。

EDIT: My code snippet is as follows编辑:我的代码片段如下

/* img1, img2 are img elements I get from other part of the program according to user selections. 
As per user input more than 2 images can also be received. Demonstrating issue using two */
const canvas = document.getElementB("canvas"); //the canvas on which I am rendering.
const gl = canvas.getContext("webgl");
drawfilter(gl,img1,img1.width, img1.height); // first displaying image one
drawfilter(gl,img2,img2.width, img2.height); // when second image is received the function is called again
    
function drawfilter(gl,img,width,height){     
    gl.clearColor(1, 1, 1, 1);
    gl.clear(gl.COLOR_BUFFER_BIT||gl.DEPTH_BUFFER_BIT||gl.STENCIL_BUFFER_BIT);
    function createShader(gl, type, shaderSource) {
        const shader = gl.createShader(type);
        gl.shaderSource(shader, shaderSource);
        gl.compileShader(shader);
    
        const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
            if (!success) {
                console.warn(gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
            }
    
       return shader;
    }
    
    //the shaderssources cannot be displayed here
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);  //simple vertex shader
    const fragmentShaderA = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceA);//simple fragment shader
    const fragmentShaderB = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceB);//simple fragment shader
    /* this shader takes two texture inputs. 1- original image, 
    2- ShadersourceA applied on original image then on output shadersouceB is applied and the result is passed as second texture to this fragment shader */
    const fragmentShaderC = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSourceC);
    
    function createProgram(gl, vertexShader, fragmentShader) {
        const program = gl.createProgram();
        gl.attachShader(program, vertexShader);
        gl.attachShader(program, fragmentShader);
        gl.linkProgram(program);
    
        const success = gl.getProgramParameter(program, gl.LINK_STATUS);
            if (!success) {
                console.log(gl.getProgramInfoLog(program));
                gl.deleteProgram(program);
            }
        return program;
    }
    
    const programA = createProgram(gl, vertexShader, fragmentShaderA);
    const programB = createProgram(gl, vertexShader, fragmentShaderB);
    const programC = createProgram(gl, vertexShader, fragmentShaderC);
    const texFbPair1 = createTextureAndFramebuffer(gl);
    const texFbPair2 = createTextureAndFramebuffer(gl);
    
    function setAttributes(program) {
        const positionLocation = gl.getAttribLocation(program, 'position');
        const positionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            -1, -1, -1, 1, 1, -1,
            1, 1, 1, -1, -1, 1,
        ]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(positionLocation);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
        const texCoordLocation = gl.getAttribLocation(program, "a_texCoord");
        const texCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
            0.0, 1.0,
            0.0, 0.0,
            1.0, 1.0,
            1.0, 0.0,
            1.0, 1.0,
            0.0, 0.0]), gl.STATIC_DRAW);
        gl.enableVertexAttribArray(texCoordLocation);
        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
   }
    
   const texture = gl.createTexture();
   texture.image = new Image();
   texture.image.onload = function () {
       handleLoadedTexture(gl, texture);
   };
   texture.image.crossOrigin = '';
   texture.image.src = img.getAttribute('src');
   function handleLoadedTexture(gl, texture, callback) {
        gl.bindTexture(gl.TEXTURE_2D, texture);
        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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
    
        setAttributes(programA);
        gl.useProgram(programA);
        gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair1.fb);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.clearColor(0, 0, 1, 1);
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.drawArrays(gl.TRIANGLES, 0, 6);
    
        setAttributes(programB);
        gl.useProgram(programB);
        gl.bindFramebuffer(gl.FRAMEBUFFER, texFbPair2.fb);
        gl.bindTexture(gl.TEXTURE_2D, texFbPair1.tex);
        gl.clearColor(0, 0, 0, 1);
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.drawArrays(gl.TRIANGLES, 0, 6)
    
        setAttributes(programC);
        gl.useProgram(programC);
        var uTextureLocation = gl.getUniformLocation(programC, "uTexture");
        var originalTextureLocation = gl.getUniformLocation(programC, "originalTexture");
        // set which texture units to render with.
        gl.uniform1i(uTextureLocation, 0);  // texture unit 0
        gl.uniform1i(originalTextureLocation, 1);  // texture unit 1
        // Set each texture unit to use a particular texture.
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texFbPair2.tex);
        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);
        gl.clearColor(0, 0, 0, 1);
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
        gl.drawArrays(gl.TRIANGLES, 0, 6)
    }
    function createTextureAndFramebuffer(gl) {
        const tex = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
        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);
        const fb = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
        return { tex: tex, fb: fb };
    }
}

Please next time post a working sample so we don't have to spend the time doing it ourselves.请下次发布一个工作样本,这样我们就不必花时间自己做。 You can load images from imgur .您可以从imgur加载图像。

The issue is the first time you call handleLoadedTexture at the bottom it sets the active texture unit to 1 with gl.activeTexture(gl.TEXTURE1) which means the second time handleLoadedTexture is called it's binding the texture to texture unit 1 where as the first 2 shaders are using texture unit 0 which still has the texture from the first time handleLoadedTexture was called bound to it.问题是您第一次在底部调用handleLoadedTexture时,它使用gl.activeTexture(gl.TEXTURE1)将活动纹理单元设置为 1,这意味着第二次调用handleLoadedTexture时,它将纹理绑定到纹理单元 1,其中第一个 2着色器正在使用纹理单元 0,它仍然具有第一次调用handleLoadedTexture时的纹理。

Otherwise, other issues with the code否则,代码的其他问题

  • I had to wait for img1 and img2 to load otherwise I can't read img.width and img.height我必须等待img1img2加载,否则我无法读取img.widthimg.height

    Now maybe in your actual code they are already loaded but if they are already loaded then there's no reason to load them again现在可能在您的实际代码中它们已经加载但如果它们已经加载那么没有理由再次加载它们

  • The code is compiling all 3 shaders, once for each call to drawfilter but arguably it should compile them once at init time and use the same shaders on all calls to drawFilter该代码正在编译所有 3 个着色器,每次调用drawfilter一次,但可以说它应该在初始化时编译一次,并在所有调用drawFilter时使用相同的着色器

  • It code is creating new buffers for every draw call.它的代码为每个绘图调用创建新的缓冲区。 You only need one set of buffers which again should happen at init time.您只需要一组缓冲区,这些缓冲区应该在初始化时再次发生。 Setting the attributes needs to happen before each draw call, creating the buffers and putting data in them does not.设置属性需要在每次绘制调用之前进行,创建缓冲区并将数据放入其中不需要。

    Well, technically, setting the attributes only needs to happen if they need to be different.好吧,从技术上讲,只有在需要不同时才需要设置属性。 If you force the position and a_texCoord attributes to be at the same locations with bindAttribLocation before calling linkProgram so that they match locations across programs then you'd only need to set the attributes once assuming you also use the same buffers with the same data (see previous point)如果您在调用linkProgram之前强制 position 和 a_texCoord 属性位于与bindAttribLocation相同的位置,以便它们匹配跨程序的位置,那么您只需要设置一次属性,假设您还使用相同的缓冲区和相同的数据(请参阅前一点)

  • || (logical or) is not the same as | (逻辑或)与|不同(binary or). (二进制或)。 For gl.clear you need to use binary or gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT|gl.STENCIL_BUFFER_BIT otherwise the value you pass to gl.clear will be wrong and your canvas won't be cleared.对于gl.clear您需要使用二进制或gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT|gl.STENCIL_BUFFER_BIT否则您传递给gl.clear的值将是错误的,您的 canvas 将不会被清除。 There isn't much reason to clear in this example though as blending is not on and the draw calls draw every pixel of the canvas在此示例中没有太多需要清除的理由,因为混合未打开,并且绘制调用绘制了 canvas 的每个像素

  • setting gl.clearColor for each framebuffer doesn't do anything unless you call gl.clear but like above, since the draw will effect every pixel and blending is off calling gl.clear would not change the results.为每个帧缓冲区设置gl.clearColor不会做任何事情,除非你调用gl.clear但像上面一样,因为绘制会影响每个像素并且混合关闭调用gl.clear不会改变结果。

  • viewport settings don't match the framebuffers.视口设置与帧缓冲区不匹配。 The framebuffer textures are created to be the same size as the images but the viewport settings were set to the size of the canvas.帧缓冲区纹理被创建为与图像相同的大小,但视口设置被设置为 canvas 的大小。 They should be set the same size as the framebuffer attachments它们应设置为与帧缓冲区附件相同的大小

 const vertexShaderSource = ` attribute vec4 position; attribute vec2 a_texCoord; varying vec2 v_texCoord; void main() { gl_Position = position; v_texCoord = a_texCoord; } `; const fragmentShaderSourceA = ` precision mediump float; uniform sampler2D uTexture; varying vec2 v_texCoord; void main() { gl_FragColor = texture2D(uTexture, v_texCoord); } `; const fragmentShaderSourceB = ` precision mediump float; uniform sampler2D uTexture; varying vec2 v_texCoord; void main() { gl_FragColor = texture2D(uTexture, v_texCoord.yx); } `; const fragmentShaderSourceC = ` precision mediump float; uniform sampler2D uTexture; uniform sampler2D originalTexture; varying vec2 v_texCoord; void main() { vec4 color1 = texture2D(uTexture, v_texCoord); vec4 color2 = texture2D(originalTexture, v_texCoord); gl_FragColor = color1 * color2; //?? } `; function loadImage(url) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { resolve(img); }; img.onerror = reject; img.crossOrigin = "anonymous"; // only needed because images are on another domain img.src = url; }); } async function main() { // we need to wait for the images to load otherwise // width and height will not be set. const [img1, img2] = await Promise.all([ 'https://i.imgur.com/KjUybBD.png', 'https://i.imgur.com/v38pV.jpg', ].map(loadImage)); /* img1, img2 are img elements I get from other part of the program according to user selections. As per user input more than 2 images can also be received. Demonstrating issue using two */ const canvas = document.getElementById("canvas"); //the canvas on which I am rendering. const gl = canvas.getContext("webgl"); drawfilter(gl, img1, img1.width, img1.height); // first displaying image one drawfilter(gl, img2, img2.width, img2.height); // when second image is received the function is called again function drawfilter(gl, img, width, height) { gl.clearColor(1, 1, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); function createShader(gl, type, shaderSource) { const shader = gl.createShader(type); gl.shaderSource(shader, shaderSource); gl.compileShader(shader); const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (.success) { console.warn(gl;getShaderInfoLog(shader)). gl;deleteShader(shader); } return shader, } //the shaderssources cannot be displayed here const vertexShader = createShader(gl. gl,VERTEX_SHADER; vertexShaderSource), //simple vertex shader const fragmentShaderA = createShader(gl. gl,FRAGMENT_SHADER; fragmentShaderSourceA), //simple fragment shader const fragmentShaderB = createShader(gl. gl,FRAGMENT_SHADER; fragmentShaderSourceB). //simple fragment shader /* this shader takes two texture inputs, 1- original image, 2- ShadersourceA applied on original image then on output shadersouceB is applied and the result is passed as second texture to this fragment shader */ const fragmentShaderC = createShader(gl. gl,FRAGMENT_SHADER; fragmentShaderSourceC), function createProgram(gl, vertexShader. fragmentShader) { const program = gl;createProgram(). gl,attachShader(program; vertexShader). gl,attachShader(program; fragmentShader). gl;linkProgram(program). const success = gl,getProgramParameter(program. gl;LINK_STATUS). if (.success) { console;log(gl.getProgramInfoLog(program)); gl;deleteProgram(program), } return program, } const programA = createProgram(gl; vertexShader, fragmentShaderA), const programB = createProgram(gl; vertexShader, fragmentShaderB), const programC = createProgram(gl; vertexShader; fragmentShaderC); const texFbPair1 = createTextureAndFramebuffer(gl). const texFbPair2 = createTextureAndFramebuffer(gl), function setAttributes(program) { const positionLocation = gl;getAttribLocation(program. 'position'); const positionBuffer = gl.createBuffer(). gl,bindBuffer(gl;ARRAY_BUFFER. positionBuffer). gl,bufferData(gl,ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1. ]); gl.STATIC_DRAW); gl.enableVertexAttribArray(positionLocation), gl,vertexAttribPointer(positionLocation. 2, gl,FLOAT, false; 0. 0), const texCoordLocation = gl;getAttribLocation(program. "a_texCoord"); const texCoordBuffer = gl.createBuffer(). gl,bindBuffer(gl;ARRAY_BUFFER. texCoordBuffer). gl,bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0 ]); gl.STATIC_DRAW); gl.enableVertexAttribArray(texCoordLocation), gl,vertexAttribPointer(texCoordLocation. 2, gl,FLOAT, false; 0. 0); } const texture = gl.createTexture(); texture.image = new Image(). texture,image;onload = function() { handleLoadedTexture(gl; texture). }. texture;image.crossOrigin = ''. texture.image;src = img,getAttribute('src'), function handleLoadedTexture(gl. texture. callback) { gl;activeTexture(gl.TEXTURE0). gl,bindTexture(gl;TEXTURE_2D. texture). 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,texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER; gl.NEAREST). gl,texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER; gl.NEAREST). gl,texImage2D(gl,TEXTURE_2D. 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE; texture;image). setAttributes(programA); gl.useProgram(programA). gl,bindFramebuffer(gl.FRAMEBUFFER; texFbPair1.fb). gl,bindTexture(gl;TEXTURE_2D. texture), gl,clearColor(0, 0; 1. 1), gl,viewport(0, 0; width. height). gl,drawArrays(gl,TRIANGLES; 0; 6). setAttributes(programB); gl.useProgram(programB). gl,bindFramebuffer(gl.FRAMEBUFFER; texFbPair2.fb). gl,bindTexture(gl.TEXTURE_2D; texFbPair1.tex), gl,clearColor(0, 0; 0. 1), gl,viewport(0, 0; width. height). gl,drawArrays(gl,TRIANGLES; 0. 6) setAttributes(programC); gl.useProgram(programC), var uTextureLocation = gl;getUniformLocation(programC. "uTexture"), var originalTextureLocation = gl;getUniformLocation(programC. "originalTexture"). // set which texture units to render with, gl;uniform1i(uTextureLocation. 0), // texture unit 0 gl;uniform1i(originalTextureLocation. 1). // texture unit 1 // Set each texture unit to use a particular texture. gl;activeTexture(gl.TEXTURE0). gl,bindTexture(gl.TEXTURE_2D; texFbPair2.tex). gl;activeTexture(gl.TEXTURE1). gl,bindTexture(gl;TEXTURE_2D. texture). gl,bindFramebuffer(gl;FRAMEBUFFER. null), gl,clearColor(0, 0; 0. 1), gl,viewport(0. 0. gl,canvas.width. gl;canvas.height). gl,drawArrays(gl,TRIANGLES. 0; 6) } function createTextureAndFramebuffer(gl) { const tex = gl.createTexture(). gl,bindTexture(gl;TEXTURE_2D. tex). gl,texImage2D(gl,TEXTURE_2D. 0, gl,RGBA, width, height. 0, gl.RGBA, gl;UNSIGNED_BYTE. null). 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); const fb = gl.createFramebuffer(). gl,bindFramebuffer(gl;FRAMEBUFFER. fb). gl,framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl,TEXTURE_2D; tex: 0), return { tex: tex; fb; fb }; } } } main();
 canvas { border: 1px solid black; }
 <canvas id="canvas"></canvas>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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