I have a canvas on which I render a given image by applying some WebGL filter on it. The canvas that is displayed must be reused. 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.
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. The function has all the WebGL processing part. So, whenever I get a new image to be processed and display on canvas. I call this function with the new img element and webglrenderingcontext of the same canvas.
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). And if I pass same image twice the canvas shows the content upside down.
I want to know how can I clear the canvas and/or the WebGL rendering context before I start using the new image. 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 .
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.
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
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
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)
||
(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. 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
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.
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. 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>
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.