前端如何制作出透明背景视频

近期项目需求做一个透明背景的视频叠加摄像头的交互,于是去了解了一下透明背景视频的实现方法。

1、webm视频格式

首先想到的当然是先跟动画大哥交流能否制作出透明背景的视频,给出的答案是webm格式的视频是可以背景透明的,OK,拿到视频,放到项目中,chorme打开,完美播放,这也太简单了吧,但移动端是不会同意让你这么轻易就播放视频的,Safari,微信,都不支持,没办法,只能继续寻找方案。

2、CSS 样式去掉背景

利用css样式 mix-blend-mode 混合模式,有一个属性 screen 就是黑色和其它元素进行混合的时候表现为透明。但这种方法有一种情况没法解决,就是如果视频中有元素是半透明的就无法成功了。

有关这种方法详细描述可以参考链接:如何让MP4 video视频背景色变成透明?

3、webgl 渲染带透明通道的视频

首先,你需要一个这样的视频

或者一个这样的视频

尽量使得视频尺寸宽高比为1:2或者2:1,这样渲染出来的视频就是1:1大小了。然后用webgl将视频渲染在画布上,渲染的同时将视频上下,或者左右进行叠加计算。

webgl渲染可以用three.js,上下叠加的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
let videoWidth=1080; //视频实际的宽度
//定义渲染器
var renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
    });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setClearColor(new THREE.Color("lightgrey"), 0);
renderer.setSize(videoWidth, videoWidth);
//居中显示
renderer.domElement.style.position = "absolute";
renderer.domElement.style.top = (window.innerHeight - videoWidth) / 2 + "px";
renderer.domElement.style.left = (window.innerWidth - videoWidth) / 2 + "px";
document.body.appendChild(renderer.domElement);
var scene = new THREE.Scene();
var camera = new THREE.Camera();
scene.add(camera);
//播放视频
var video = document.getElementById("video");
video.play();
//获取视频纹理
var texture = new THREE.VideoTexture(video);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
//定义几何体
var geometry = new THREE.PlaneBufferGeometry(1, 1);

//处理视频纹理
var uniforms = {
        time: { type: "f", value: 1.0 },
        texture: { type: "sampler2D", value: texture }
    };
var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader:
    `varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }`,
    fragmentShader:
    `#ifdef GL_ES
    precision highp float;
    #endif
   
    uniform float time;
    uniform sampler2D texture;
    varying vec2 vUv;
   
    void main( void ) {
      gl_FragColor = vec4(
      texture2D(texture, vec2(vUv.x, 0.5 + vUv.y/2.)).rgb,
      texture2D(texture, vec2(vUv.x, vUv.y/2.)).r
      );
    }`,
    transparent: true
});
var mesh = new THREE.Mesh(geometry, material)
scene.add(mesh);


var animate = function() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
    };
requestAnimationFrame(animate);

如果是左右,只需要改一下叠加的方向就行,代码中的 fragmentShader 属性改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fragmentShader:
    `#ifdef GL_ES
    precision highp float;
    #endif
   
    uniform float time;
    uniform sampler2D texture;
    varying vec2 vUv;
   
    void main( void ) {
      gl_FragColor = vec4(
      texture2D(texture, vec2(vUv.x/2., vUv.y)).rgb,
      texture2D(texture, vec2(vUv.x/2., vUv.y)).r
      );
    }`,

以上,完美解决视频背景透明问题。

本文结。