随着互联网的发展,动画效果也在一直更新,从刚开始的flsh动画,cocos骨骼动画,到YY开源的svga动画。最近1年来,带有透明通道的mp4动画被使用的极为广泛,对于app端。github上有开源的AlphaPlayer可以使用,但是对于web端,相关的资料很少。为了解决这个问题想了很多办法,提出了使用webm格式。但是最终还是要实现播放mp4格式。
播放mp4格式的透明动画,需要什么呢。首先,对于web前端来说,webgl应该大家都很少介绍到,更不要说着色器了(shader)和OpenGL ES的语法了。放弃了好几次,最后看到一个帖子,可以用THREEJS来进行。先上一个图
可以看到素材中左边部分使用RGB通道存储了原透明视频的Alpha值,右边部分使用RGB通道存储了原透明视频的RGB值,然后在端上通过OpenGL重新将每个像素点的Alpha值和RGB值进行组合,重新得到ARGB视频画面,实现透明视频的动画效果。
在web端,具体实现。
1.有个video播放器,播放mp4动画
2.使用threejs和openel把画面重绘到一个容器里。
1 var lastUpdate;
2 var container; 3 var camera, scene, renderer; 4 var uniforms; 5 6 function init() { 7 8 container = document.getElementById('container'); 9 camera = new THREE.Camera(); 10 camera.position.z = 1; 11 scene = new THREE.Scene(); 12 13 var video = document.getElementById('video'); 14 video.src = "https://devimage.91banban.com/topcard.mp4"; 15 video.play(); 16 video.onended = () => { 17 video.play(); 18 } 19 videoTexture = new THREE.VideoTexture(video); 20 videoTexture.minFilter = THREE.LinearFilter; 21 videoTexture.magFilter = THREE.LinearFilter; 22 videoTexture.format = THREE.RGBAFormat; 23 24 // shader stuff 25 uniforms = { 26 time: { type: "f", value: 1.0 }, 27 texture: { type: "sampler2D", value: videoTexture } 28 }; 29 var material = new THREE.ShaderMaterial({ 30 uniforms: uniforms, 31 vertexShader: `varying vec2 vUv; 32 void main() { 33 vUv = uv; 34 gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); 35 }`, 36 fragmentShader: `#ifdef GL_ES 37 precision highp float; 38 #endif 39 40 uniform float time; 41 uniform sampler2D texture; 42 varying vec2 vUv; 43 44 void main( void ) { 45 gl_FragColor = vec4( 46 texture2D(texture, vec2(0.5+vUv.x/2., vUv.y)).rgb, 47 texture2D(texture, vec2(vUv.x/2., vUv.y)).r 48 ); 49 }`, 50 transparent: true 51 }); 52 lastUpdate = new Date().getTime(); 53 var geometry = new THREE.PlaneBufferGeometry(2, 2); 54 var mesh = new THREE.Mesh(geometry, material); 55 mesh.scale.setScalar(0.8); 56 scene.add(mesh); 57 renderer = new THREE.WebGLRenderer({ alpha: true }); 58 renderer.setPixelRatio(window.devicePixelRatio / 1); 59 60 container.appendChild(renderer.domElement); 61 62 document.getElementById('play-button').addEventListener('click', e => { video.play(); }); 63 renderer.setSize(1000, 1100); 64 this.animate(); 65 } 66 67 function animate() { 68 var currentTime = new Date().getTime() 69 var timeSinceLastUpdate = currentTime - lastUpdate; 70 lastUpdate = currentTime; 71 requestAnimationFrame(animate); 72 render(timeSinceLastUpdate); 73 74 } 75 function render(timeDelta) { 76 uniforms.time.value += (timeDelta ? timeDelta / 1000 : 0.05); 77 renderer.render(scene, camera); 78 } 79 80 init();
最终实现的效果图