使用 WebGL 将两个画布混合到一个上

Blend two canvases onto one with WebGL(使用 WebGL 将两个画布混合到一个上)
本文介绍了使用 WebGL 将两个画布混合到一个上的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要做的是将两个画布混合到一个画布上,用于我正在创建的绘图应用程序.我非常了解 Javascript,但我真的不知道从哪里开始使用 WebGL,而且由于这不是一项非常艰巨的任务,我假设如果我不使用它会产生更快的处理速度另一个库,例如 Three.js 或其他类似的库.

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.

我已经拥有的是用户将要绘制的画布(我们称它们为画布 A 和 B),它们都是隐藏的,而画布 C 正在显示.

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>

我已经完成了主绘图应用程序,供用户选择要在其上绘制的图层并在其上绘图,但是我如何能够使用 WebGL 使用某种混合模式将两个图层混合在一起(即:乘) 当用户继续使用 WebGL 编辑画布?

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?

起初我尝试在这里关注其他帖子:https://stackoverflow.com/a/11596922/1572938但我很困惑.

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

如果有人想填补我的 jsfiddle 上的空白,那将非常有效!http://jsfiddle.net/W3fVV/1/

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/

推荐答案

这里有一个用图片绘制的例子:https://webglfundamentals.org/webgl/lessons/webgl-image-processing.html

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

WebGL 不关心源是图像、画布还是视频.所以改变样本从

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

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

您需要设置这 2 个纹理并告诉您的 GLSL 程序您将它们放在哪些纹理单元上.

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

示例:

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>

这篇关于使用 WebGL 将两个画布混合到一个上的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)