结合上一篇文章Three.js实现仿IOS live图效果,我们来体验一波WebVR~
本文基于webvr-polyfill,苦于没钱购买专业版的VR设备,就只能做个可以用普通VR眼镜看的程序了。所用到的原理倒也简单,webvr-polyfill会把屏幕分为左右两视图,并加入基于陀螺仪的控制器,当佩戴VR眼镜时即可看到3D效果。
加入背景
上一篇中实现了Photo.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 68 69 70 71 72 73
| var particles = 20000; var bufferGeometry = new THREE.BufferGeometry(); var positions = new Float32Array( particles * 3 ); var colors = new Float32Array( particles * 3 ); var color = new THREE.Color(); var gap = 1000; for ( var i = 0; i < positions.length; i += 3 ) { var x = ( Math.random() * gap *2 )* (Math.random()<.5? -1 : 1); var y = ( Math.random() * gap *2 )* (Math.random()<.5? -1 : 1); var z = ( Math.random() * gap *2 )* (Math.random()<.5? -1 : 1); var biggest = Math.abs(x) > Math.abs(y) ? Math.abs(x) > Math.abs(z) ? 'x' : 'z' : Math.abs(y) > Math.abs(z) ? 'y' : 'z'; var pos = { x, y, z}; if(Math.abs(pos[biggest]) < gap) pos[biggest] = pos[biggest] < 0 ? -gap : gap; x = pos['x']; y = pos['y']; z = pos['z']; positions[ i ] = x; positions[ i + 1 ] = y; positions[ i + 2 ] = z; var hasColor = Math.random() > 0.3; var vx, vy, vz; if(hasColor){ vx = (Math.random()+1) / 2 ; vy = (Math.random()+1) / 2 ; vz = (Math.random()+1) / 2 ; }else{ vx = 1 ; vy = 1 ; vz = 1 ; } var vy = ( Math.abs(y) / n*2 ) ; var vz = ( Math.abs(z) / n*2 ) ;*/ color.setRGB( vx, vy, vz ); colors[ i ] = color.r; colors[ i + 1 ] = color.g; colors[ i + 2 ] = color.b; } bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); bufferGeometry.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ) ); bufferGeometry.computeBoundingSphere(); var material = new THREE.PointsMaterial( { size: 6, vertexColors: THREE.VertexColors } ); var particleSystem = new THREE.Points( bufferGeometry, material ); scene.add( particleSystem );
|
以上代码引自SolarSystem,实例化一个BufferGeometry对象,随机生成星星的位置与颜色并绑定到该对象上,而后以BufferGeometry对象为参数实例化Points对象并加入到场景中。
随机分布Photo对象
1 2 3 4 5 6 7 8 9
| function setRandomPosition(mesh, distance) { var ang1 = Math.PI * Math.random() - Math.PI / 2, ang2 = Math.PI * Math.random() * 2, dist = Math.cos(ang1) * distance; mesh.position.y = Math.sin(ang1) * distance; mesh.position.x = Math.cos(ang2) * dist; mesh.position.z = Math.sin(ang2) * dist; }
|
初始化Photo对象时调用以上函数,将其随机分布到以半径为distance的球体表面。
控制Photo对象的显示与隐藏
现在要使Photo对象位于显示器中部时显示并播放视频,其它时候隐藏。
在循环体中加入以下代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var smallCamera = camera.clone(); smallCamera.fov = 20; smallCamera.updateProjectionMatrix(); for(var i =0;i<meshes.length;i++){ var cont = meshes[i]; showUp = cont.position.clone().project(smallCamera) var shouldShow = (showUp.x < 1 && showUp.x > -1 && showUp.y < 1 && showUp.y > -1 && showUp.z < 1 && showUp.z > -1) if(shouldShow ){ cont.show() } if(!cont.counter){ cont.play() } if( !shouldShow ){ cont.hide() } }
|
以上代码用于判断对象是否出现在camera可视范围内,但有个bug,就是存在于camera正后方的元素,shouldShow也会为true。
加入WebVR
重头戏来了,现在我们要加入WebVR使之成为3D场景。
引入js框架
1 2 3
| <script src="js/VRControls.js"></script> <script src="js/VREffect.js"></script> <script src="js/webvr-polyfill.js"></script>
|
实例化组件
1 2 3 4 5 6 7 8 9 10 11
| controls = new THREE.VRControls(camera); effect = new THREE.VREffect(renderer); effect.setSize(window.innerWidth, window.innerHeight); navigator.getVRDisplays().then(function(displays) { if (displays.length > 0) { vrDisplay = displays[0]; vrDisplay.requestAnimationFrame(loop); } });
|
调用
1 2 3
| document.querySelector('button#vr').addEventListener('click', function() { vrDisplay.requestPresent([{source: renderer.domElement}]); });
|
效果展示
性能优化方案
开启VR模式后,卡顿现象异常严重,甚至无法正常使用,主要可能是因为Photo渐入渐出动画的计算量过于巨大,这就有几点优化的方案:
- 减少动画帧数,让动画尽快完成。
- 离散分布对象,减少对象重叠的可能性,防止多个对象同时渐入渐出。
- 以上办法如果都不能缓解卡顿现象的话,就只能去掉动画了。
源码放在github,有兴趣的朋友可以去看看。