合并两个 canvas。把第一个放在第二个上

Combine two canvas. Put the first on the second

我正在尝试做类似这种抽象的事情。

https://www.ciklum.com

据我了解,需要将两者结合起来canvas,将它们放在一起。但是如何做到这一切?

这里我举两个实现为例。 第一

let vertices = [];
let gl = particle.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());

resize();

function resize() {
  
  particle.width = innerWidth;
  particle.height = innerHeight;
  
  let step = 10,
      w = Math.floor(particle.width/step), 
      h = Math.floor(particle.height/step);
  
  vertices = [];
  for (var x=0; x<w*3; x++) 
    for (var y=0; y<10; y++) 
      vertices.push(1/w + x*10/w - 5, 1/h + y*2/h - 1)

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

let pid = gl.createProgram();

shader(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( 1.0,     0.0,    0.0), 
                vec3( -1.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1.0, 0.5, 0.0);
  }
`, gl.VERTEX_SHADER);

shader(`
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let timeUniform = gl.getUniformLocation(pid, 'time');


requestAnimationFrame(draw);
addEventListener('resize', resize)

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(timeUniform, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);

  requestAnimationFrame(draw);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split("\n")
                   .map((str, i) => (""+(1+i))
                   .padStart(4, "0")+": "+str)
                   .join("\n"));
    throw message;
  }
}
<canvas id="particle" />

<style>
    canvas {
        background-image:url(https://i.imgur.com/HiAlf85.jpg);
    }
</style>

第二

let vertices = [];
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());

resize();

function resize() {
  
  canvas.width = innerWidth;
  canvas.height = innerHeight;
  
  let step = 10,
      w = Math.floor(canvas.width/step), 
      h = Math.floor(canvas.height/step);
  
  vertices = [];
  for (var x=0; x<w*3; x++) 
    for (var y=0; y<10; y++) 
      vertices.push(1/w + x*10/w - 5, 1/h + y*2/h - 1)

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

let pid = gl.createProgram();

shader(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( -1.0,     -1.0,    0.0), 
                vec3( 0.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1., 0., 0.);
  }
`, gl.VERTEX_SHADER);

shader(`
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let timeUniform = gl.getUniformLocation(pid, 'time');


requestAnimationFrame(draw);
addEventListener('resize', resize)

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(timeUniform, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);

  requestAnimationFrame(draw);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split("\n")
                   .map((str, i) => (""+(1+i))
                   .padStart(4, "0")+": "+str)
                   .join("\n"));
    throw message;
  }
}
<canvas id="canvas" />

<style>
    canvas {
    background-image:url(https://i.imgur.com/HiAlf85.jpg);
}
</style>

告诉我如何组合它们?

如果您访问您链接的网站并查看他们的代码,您会注意到他们只是将两个 canvas 放在同一个 div 中,并将它们放在彼此有点css

canvas {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: -1px;
}

在链接示例中,由于绝对定位,两个 canvas 相互重叠。

但是对于您的示例,即使您将它们放置在彼此之上,您也只会看到一个,因为您为每个 canvas.

添加了一个非透明背景图像

链接示例依赖于每个 canvas 是透明的。这意味着,只对 canvas 中需要着色的像素进行着色,除非有透明度,否则没有背景颜色或图像。

您需要设置 CSS 以便 2 canvas 重叠并从顶部移除背景 canvas

body {
  margin: 0;
}
canvas {
  display: block;
}

#particle {
  background-image:url(https://i.imgur.com/HiAlf85.jpg);
}
#canvas {
  position: absolute;
  left: 0;
  top: 0;
}

此外,您需要弄清楚如何合并您的 2 个片段。就像快速破解一样,我将两者都包含在它们自己的函数中,这样变量就不会冲突,但这意味着一堆代码是重复的。

最后一件事是自动关闭 canvas 因为 <canvas /> 是非法的 HTML。您需要使用 <canvas></canvas> 中的实际结束标记。

您可以通过检查

来查看

<canvas />
<canvas />

在浏览器的开发工具中

注意一个 canvas 在另一个里面而不是分开的。那是因为 /> 实际上并没有结束 canvas 标签。 Canvas 标签不在内部显示任何 HTML,因此不显示内部 canvas。

'use strict';
(function() {
let vertices = [];
let gl = particle.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());

resize();

function resize() {
  
  particle.width = innerWidth;
  particle.height = innerHeight;
  
  let step = 10,
      w = Math.floor(particle.width/step), 
      h = Math.floor(particle.height/step);
  
  vertices = [];
  for (var x=0; x<w*3; x++) 
    for (var y=0; y<10; y++) 
      vertices.push(1/w + x*10/w - 5, 1/h + y*2/h - 1)

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

let pid = gl.createProgram();

shader(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( 1.0,     0.0,    0.0), 
                vec3( -1.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1.0, 0.5, 0.0);
  }
`, gl.VERTEX_SHADER);

shader(`
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let timeUniform = gl.getUniformLocation(pid, 'time');


requestAnimationFrame(draw);
addEventListener('resize', resize)

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(timeUniform, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);

  requestAnimationFrame(draw);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split("\n")
                   .map((str, i) => (""+(1+i))
                   .padStart(4, "0")+": "+str)
                   .join("\n"));
    throw message;
  }
}
}());

// ----------------------

(function() {
let vertices = [];
let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());

resize();

function resize() {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
  
  let step = 10,
      w = Math.floor(canvas.width/step), 
      h = Math.floor(canvas.height/step);
  
  vertices = [];
  for (var x=0; x<w*3; x++) 
    for (var y=0; y<10; y++) 
      vertices.push(1/w + x*10/w - 5, 1/h + y*2/h - 1)

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

let pid = gl.createProgram();

shader(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( -1.0,     -1.0,    0.0), 
                vec3( 0.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1., 0., 0.);
  }
`, gl.VERTEX_SHADER);

shader(`
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`, gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let timeUniform = gl.getUniformLocation(pid, 'time');


requestAnimationFrame(draw);
addEventListener('resize', resize)

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(timeUniform, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);

  requestAnimationFrame(draw);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split("\n")
                   .map((str, i) => (""+(1+i))
                   .padStart(4, "0")+": "+str)
                   .join("\n"));
    throw message;
  }
}
}());
body {
  margin: 0;
}
canvas {
  display: block;
}

#particle {
  background-image:url(https://i.imgur.com/HiAlf85.jpg);
  background-size: cover;
}
#canvas {
  position: absolute;
  left: 0;
  top: 0;
}
<canvas id="particle"></canvas>
<canvas id="canvas"></canvas>

当然不清楚你为什么要这样做。使用 1 canvas 可能会更快,但我假设您有自己的理由。

'use strict';
let vertices = [];
let gl = particle.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());

resize();

function resize() {
  
  particle.width = innerWidth;
  particle.height = innerHeight;
  
  let step = 10,
      w = Math.floor(particle.width/step), 
      h = Math.floor(particle.height/step);
  
  vertices = [];
  for (var x=0; x<w*3; x++) 
    for (var y=0; y<10; y++) 
      vertices.push(1/w + x*10/w - 5, 1/h + y*2/h - 1)    

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
}

let pid1 = createProgram(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( 1.0,     0.0,    0.0), 
                vec3( -1.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1.0, 0.5, 0.0);
  }
`,
`
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`);

let pid2 = createProgram(`
  attribute vec2 v; 
  uniform float time; 
  varying vec3 c; 

  mat3 rotateX(float a) {
    return mat3(vec3( -1.0,     -1.0,    0.0), 
                vec3( 0.0,  cos(a), -sin(a)),
                vec3( 0.0,  sin(a),  cos(a)));
  }
  
  mat3 rotateY(float a){
    return mat3(vec3( cos(a), 0.0, sin(a)), 
                vec3(    0.0, 1.0,    0.0),
                vec3(-sin(a), 0.0, cos(a)));
  }

  mat3 rotateZ(float a){
    return mat3(vec3( cos(a), -sin(a),  0.0), 
                vec3( sin(a),  cos(a),  0.0),
                vec3(    0.0,     0.0,  1.0));
  }
  
  void main(void) {
    vec2 p = v;
    p.y += 0.3;
    p.x += sin(time/4. + p.y);
    vec3 pos = vec3(p.xy, 0.0)*rotateX(p.x*3. + time);
    //pos.y += sin(pos.x) - sin(time/5.)*0.5 + cos(pos.y/3.1415)*0.5;
    gl_Position = vec4(pos, 1.);
    
    gl_PointSize = 2.7;
    gl_Position.z = 0.0;
    c.rgb=vec3(1., 0., 0.);
  }
`, `
  precision highp float;
  varying vec3 c;
  void main(void) {
      gl_FragColor = vec4(c, 1.);  
  }
`);

let v1 = gl.getAttribLocation(pid1, "v");
let timeUniform1 = gl.getUniformLocation(pid1, 'time');

let v2 = gl.getAttribLocation(pid2, "v");
let timeUniform2 = gl.getUniformLocation(pid2, 'time');

requestAnimationFrame(draw);
addEventListener('resize', resize)

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  
  gl.useProgram(pid1);
  // normally you'd bind a buffer here but since you only
  // have one and the vertex data is the same for both programs
  // we don't need to bind a different buffer
  gl.vertexAttribPointer(v1, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(v1);   
  gl.uniform1f(timeUniform1, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);
  
  gl.useProgram(pid2);
  gl.vertexAttribPointer(v2, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(v2);   
  gl.uniform1f(timeUniform2, t/1000);
  gl.drawArrays(gl.POINTS, 0, vertices.length/2);

  requestAnimationFrame(draw);
}

function createProgram(vs, fs) {
  const pid = gl.createProgram();
  shader(vs, gl.VERTEX_SHADER, pid);
  shader(fs, gl.FRAGMENT_SHADER, pid);
  gl.linkProgram(pid);
  // should check for link error here!
  return pid;
}

function shader(src, type, pid) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  var message = gl.getShaderInfoLog(sid);
  gl.attachShader(pid, sid);
  if (message.length > 0) {
    console.log(src.split("\n")
                   .map((str, i) => (""+(1+i))
                   .padStart(4, "0")+": "+str)
                   .join("\n"));
    throw message;
  }
}
body {
  margin: 0;
}
canvas {
  display: block;
}
#particle {
  background-image:url(https://i.imgur.com/HiAlf85.jpg);
  background-size: cover;
}
<canvas id="particle"></canvas>