Evan Kennedy Evan Kennedy - 25 days ago 6
Javascript Question

Blend two canvases onto one with WebGL

What I'm trying to do is blend two canvases onto a single canvas for a drawing app I am creating. I know Javascript very well, but I really don't have any clue where to start with WebGL and since it isn't a very hard task to do, I'm assuming it would be yield quicker processing speeds if I don't use another library like Three.js or others of that sort.

What I already have are canvases that the user will be drawing on (Let's call them canvas A and B) which are both hidden and canvas C which is being shown.

<canvas id='C' width=800 height=600></canvas>

<canvas id='A' width=800 height=600 style='display:none'></canvas>
<canvas id='B' width=800 height=600 style='display:none'></canvas>


I already have the main drawing app done for the user to pick the layer to draw on and to draw on it, but how would I be able to use WebGL to blend the two layers together using some blend mode (ie: multiply) as the user continues to edit the canvases using WebGL?

At first I tried following the other post here: http://stackoverflow.com/a/11596922/1572938 but I got confused.

If someone wants to fill in the gaps on my jsfiddle for the other post that would work very well! http://jsfiddle.net/W3fVV/1/

Answer

There's an example of drawing with images here: http://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

WebGL doesn't care if the sources are images, canvases or video. So change the samples from

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, someImage);

to

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, someCanvas);

Then write a fragment shader to blend the 2 textures as in

precision mediump float;

// our 2 canvases
uniform sampler2D u_canvas1;
uniform sampler2D u_canvas2;

// the texCoords passed in from the vertex shader.
// note: we're only using 1 set of texCoords which means
//   we're assuming the canvases are the same size.
varying vec2 v_texCoord;

void main() {
     // Look up a pixel from first canvas
     vec4 color1 = texture2D(u_canvas1, v_texCoord);

     // Look up a pixel from second canvas
     vec4 color2 = texture2D(u_canvas2, v_texCoord);

     // return the 2 colors multiplied
     gl_FragColor = color1 * color2;
}

You'll need to setup the 2 textures and tell your GLSL program which texture units you put them on.

function setupTexture(canvas, textureUnit, program, uniformName) {
   var tex = gl.createTexture();

   updateTextureFromCanvas(tex, canvas, textureUnit);

   // Set the parameters so we can render any size image.
   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_MIN_FILTER, gl.NEAREST);
   gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

   var location = gl.getUniformLocation(program, uniformName);
   gl.uniform1i(location, textureUnit);
}

function updateTextureFromCanvas(tex, canvas, textureUnit) {
  gl.activeTexture(gl.TEXTURE0 + textureUnit);
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
}

var tex1 = setupTexture(canvas1, 0, program, "u_canvas1");
var tex2 = setupTexture(canvas2, 1, program, "u_canvas2");

Sample here: http://jsfiddle.net/greggman/NLNmd/2/