6

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

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

<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 编辑画布?

起初我尝试在这里关注另一篇文章:https ://stackoverflow.com/a/11596922/1572938但我很困惑。

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

4

2 回答 2

13

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

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

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

然后编写一个片段着色器来混合 2 个纹理,如

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 程序您将它们放在哪些纹理单元上。

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>

于 2012-09-26T02:23:46.767 回答
3

我认为只使用 canvas 2D API 会更简单。

您可以先在画布 C 中绘制画布 A,然后在画布 C 中绘制画布 B 之前更改画布 C 的全局不透明度。

您还可以使用更改画布混合方式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);
于 2012-09-28T15:29:39.217 回答