Mix two canvases on one using WebGL

What I'm trying to do is put two canvases on one canvas for the drawing application I am creating. I know Javascript very well, but I don’t have any clue where to start with WebGL, and since this is not a very difficult task, I assume that this will lead to faster processing speed if I do not use another library, for example Three .js or others.

I already have canvases on which the user will be drawn (let them be called canvases A and B), which are displayed both hidden and canvases C that are displayed.

<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 an application for the main drawing, which the user can choose to draw a layer and draw on it, but how can I use WebGL to combine two layers together using some blending mode (i.e. multiplication), as the user continues to edit canvases with using webgl?

At first, I tried the following post here: https://stackoverflow.com/a/212632/2/2/2 , but I got confused.

If someone wants to fill in the blanks in my jsfiddle for another post that will work just fine! http://jsfiddle.net/W3fVV/1/

+6
source share
2 answers

Here is an example image drawing here: https://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

WebGL doesn't care if the sources are images, canvases, or videos. Therefore change the patterns 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 mix 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 need to set up 2 textures and tell the GLSL program which texture units you insert them into.

 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"); 

An example is here:

 function main() { var canvas1 = document.getElementById("canvas1"); var canvas2 = document.getElementById("canvas2"); var ctx1 = canvas1.getContext("2d"); var ctx2 = canvas2.getContext("2d"); ctx1.fillStyle = "purple"; ctx1.arc(64, 64, 30, 0, Math.PI * 2, false); ctx1.fill(); ctx2.fillStyle = "cyan"; ctx2.fillRect(50, 10, 28, 108); // Get A WebGL context var canvas = document.getElementById("webgl"); var gl = canvas.getContext("webgl"); if (!gl) { return; } // setup GLSL program var program = twgl.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]); gl.useProgram(program); // look up where the vertex data needs to go. var positionLocation = gl.getAttribLocation(program, "a_position"); var texCoordLocation = gl.getAttribLocation(program, "a_texCoord"); // provide texture coordinates for the rectangle. var texCoordBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0]), gl.STATIC_DRAW); gl.enableVertexAttribArray(texCoordLocation); gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); // lookup uniforms var resolutionLocation = gl.getUniformLocation(program, "u_resolution"); // set the resolution gl.uniform2f(resolutionLocation, canvas1.width, canvas1.height); // Create a buffer for the position of the rectangle corners. var buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.enableVertexAttribArray(positionLocation); gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); // Set a rectangle the same size as the image. setRectangle(gl, 0, 0, canvas.width, canvas.height); 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"); // Draw the rectangle. gl.drawArrays(gl.TRIANGLES, 0, 6); } function setRectangle(gl, x, y, width, height) { var x1 = x; var x2 = x + width; var y1 = y; var y2 = y + height; gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ x1, y1, x2, y1, x1, y2, x1, y2, x2, y1, x2, y2]), gl.STATIC_DRAW); } main(); 
 canvas { border: 2px solid black; width: 128px; height: 128px; } 
 <script src="https://twgljs.org/dist/3.x/twgl.min.js"></script> <!-- vertex shader --> <script id="2d-vertex-shader" type="x-shader/x-vertex"> attribute vec2 a_position; attribute vec2 a_texCoord; uniform vec2 u_resolution; varying vec2 v_texCoord; void main() { // convert the rectangle from pixels to 0.0 to 1.0 vec2 zeroToOne = a_position / u_resolution; // convert from 0->1 to 0->2 vec2 zeroToTwo = zeroToOne * 2.0; // convert from 0->2 to -1->+1 (clipspace) vec2 clipSpace = zeroToTwo - 1.0; gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1); // pass the texCoord to the fragment shader // The GPU will interpolate this value between points. v_texCoord = a_texCoord; } </script> <!-- fragment shader --> <script id="2d-fragment-shader" type="x-shader/x-fragment"> 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; } </script> <!-- fragment shader --> <script id="aa2d-fragment-shader" type="x-shader/x-fragment"> precision mediump float; // our texture uniform sampler2D u_canvas1; uniform sampler2D u_canvas2; // the texCoords passed in from the vertex shader. varying vec2 v_texCoord; void main() { gl_FragColor = texture2D(u_canvas1, v_texCoord); } </script> <canvas id="canvas1" width="128" height="128"></canvas> <canvas id="canvas2" width="128" height="128"></canvas> <canvas id="webgl" width="128" height="128"></canvas> 
+9
source

I think it will be easier for you to use the 2D canvas interface.

You can first draw canvas A in canvas C, then change the canvas C to global opacity before drawing canvas B in canvas C.

You can also change the way the canvas is mixed using globalCompositeOperation

 var canvasA = document.getElementById('C'); var canvasB = document.getElementById('C'); // The target canvas var canvasC = document.getElementById('C'); var ctx = canvasC.getContext('2d'); ctx.drawImage(canvasA, 0,0); ctx.globalAlpha = 0.5; ctx.drawImage(canvasB,0, 0); 
+2
source

Source: https://habr.com/ru/post/926241/


All Articles