【问题标题】:Clear canvas and rerender the webgl context with different image清除画布并使用不同的图像重新渲染 webgl 上下文
【发布时间】:2020-10-28 18:02:37
【问题描述】:

我有一个画布,通过在上面应用一些 WebGL 过滤器来呈现给定的图像。显示的画布必须重复使用。那就是我不断得到不同的图像(程序的其他部分),我应该在这个画布上绘制这些图像,并应用相同的过滤器(片段着色器)。

我在这里创建了一个function drawoncanvas(gl, img, img.width, img.height),gl 是画布的 webglrenderingcontext,img 是带有要使用的图像的 html 元素。该函数具有所有 WebGL 处理部分。 因此,每当我获得要处理并显示在画布上的新图像时。我用同一画布的新 img 元素和 webglrenderingcontext 调用此函数。

我面临的问题是我可以在当前内容后面的画布上看到先前绘制的内容(无论当前内容是透明的)。如果我通过两次相同的图像,画布显示的内容是颠倒的。

我想知道如何在开始使用新图像之前清除画布和/或 WebGL 渲染上下文。这样它就不会显示下面的旧内容或给出这些问题。

编辑:我的代码 sn-p 如下

/* 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 };
    }
}

【问题讨论】:

  • 这能回答你的问题吗? How to clear the canvas for redrawing
  • 我无法获取 2d 上下文并使用 clearrect。 canvas.getContext(“2d”) 给出 null
  • 如果您尝试var gl = canvas.getContext("webgl"); gl.clearColor(1, 1, 1, 1); gl.clear(gl.COLOR_BUFFER_BIT); 会怎样? Some MDN Docs
  • @blex 我已经试过了,但没用
  • 没有代码问题本身我们无法调试您的代码。 WebGL clears itself by default 所以你必须做一些特别的事情来防止这种情况发生。投票结束与 S.O. 一样的话题。如果你想调试帮助,需要一个 repo。请在snippet 中添加minimal 存储库。您可以使用来自imgur的图片

标签: javascript html5-canvas webgl


【解决方案1】:

请下次发布一个工作示例,这样我们就不必花时间自己做。你可以从imgur加载图片。

问题是您第一次在底部调用 handleLoadedTexture 时,它会将活动纹理单元设置为 1 和 gl.activeTexture(gl.TEXTURE1),这意味着第二次调用 handleLoadedTexture 它会将纹理绑定到纹理单元 1,其中前 2 个着色器正在使用纹理单元 0,它仍然具有第一次调用 handleLoadedTexture 时的纹理。

否则,代码的其他问题

  • 我必须等待 img1img2 加载,否则我无法阅读 img.widthimg.height

    现在可能在您的实际代码中它们已经加载,但如果它们已经加载,则没有理由再次加载它们

  • 代码正在编译所有 3 个着色器,每次调用 drawfilter 一次,但可以说它应该在初始化时编译一次,并在所有调用 drawFilter 时使用相同的着色器

  • 它的代码为每个绘图调用创建新的缓冲区。您只需要一组缓冲区,这些缓冲区应该在初始化时再次发生。设置属性需要在每次绘制调用之前进行,不需要创建缓冲区并将数据放入其中。

    嗯,从技术上讲,设置属性只有在它们需要不同时才需要进行。如果您在调用 linkProgram 之前强制将 position 和 a_texCoord 属性与 bindAttribLocation 位于相同的位置,以便它们匹配跨程序的位置,那么您只需要设置一次属性,假设您还使用相同的缓冲区和相同的数据(见上一点)

  • ||(逻辑或)与|(二进制或)不同。对于gl.clear,您需要使用二进制或gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT|gl.STENCIL_BUFFER_BIT,否则您传递给gl.clear 的值将是错误的,并且您的画布不会被清除。在此示例中没有太多需要清除的理由,因为混合没有打开,并且绘制调用绘制了画布的每个像素

  • 为每个帧缓冲区设置 gl.clearColor 不会做任何事情,除非您调用 gl.clear 但像上面一样,因为绘制将影响每个像素并且混合关闭调用 gl.clear 不会改变结果。

  • 视口设置与帧缓冲区不匹配。帧缓冲区纹理被创建为与图像相同的大小,但视口设置被设置为画布的大小。它们应设置为与帧缓冲区附件相同的大小

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>

【讨论】:

  • >。你的意思是:draw 调用画布的每个像素?它是“gl.drawArrays(gl.TRIANGLES, 0, 6)”而不是 gl.POINTS。
猜你喜欢
  • 2011-02-20
  • 2019-05-07
  • 1970-01-01
  • 2021-05-03
  • 2014-11-07
  • 1970-01-01
  • 2012-03-14
  • 2011-07-28
  • 2021-06-30
相关资源
最近更新 更多