近期项目需求做一个透明背景的视频叠加摄像头的交互,于是去了解了一下透明背景视频的实现方法。
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 ); }`, |
以上,完美解决视频背景透明问题。
本文结。