Three.js制作vr版太阳系

首先,看下demo效果,扫一下下面的二维码: 
下载.png

主要利用浏览器的deviceorientation事件监视设备朝向,记录下其受地心引力作用下而在方向上产生变化的数据,然后使用DeviceOrientationControls.js(一个three.js的插件)让摄像头根据设备的移动进行调整,相当于模拟人看向物体的某个方向,犹如人的视线一样。最后,结合three.js来构建一个3d世界。

1. 画一个会动的太阳

引用three.js, 建立3个要素:场景(scene)、相机(camera)和渲染器(renderer)。有了这三样东西,才能将物体渲染到网页中,然后再画一个太阳。


       var canvas = document.querySelector('#main-canvas');
       var scene = new THREE.Scene();
       var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1 ,2000);

   scene.add(camera);

       //画太阳
       var initPlanet = function(name, revolutionSpeed, color, distance, volume, map) {
       var material;

       material = new THREE.MeshLambertMaterial({
           emissive: 0xdd4422,  //发光
           map: THREE.ImageUtils.loadTexture(__uri('../../img/sun.jpg')) //太阳的皮肤
       });

       //SphereGeometry用来在三维空间内创建一个球体对象.
       var mesh = new THREE.Mesh( new THREE.SphereGeometry( volume, 50,50 ), material);
       mesh.position.x = -distance;
       mesh.receiveShadow = true;
       mesh.castShadow = true;

       scene.add(mesh);
   };
       initPlanet();

       //由于是会动的,所以调用requestAnimationFrame函数
       (function render(timestamp) {
       requestAnimationFrame(render);
       //太阳自转
       stars.Sun.Mesh.rotation.y = (stars.Sun.Mesh.rotation.y == 2*Math.PI ? 0.0008*Math.PI : stars.Sun.Mesh.rotation.y+0.0008*Math.PI);
       renderer.render(scene, camera);
   }());

就这样简单画出了一个太阳。扫二维码看效果。 
下载 (1).png

2. 构建一个太阳系

为了效果好看一点,顺便把其他行星也画上去,实现原理跟太阳的一样。接着就让各行星围绕着太阳转啊转。


       //RingGeometry用来在三维空间内创建一个二维圆环面对象.
       //轨道, 以太阳为中心的运动轨道
   var track = new THREE.Mesh( new THREE.RingGeometry(distance - 0.2, distance + 0.2, 100, 1),
           new THREE.MeshBasicMaterial( { color: 0xffffff,transparent: true, opacity: 0.1, side: THREE.DoubleSide } )
       );
   track.rotation.x = - Math.PI / 2;

   scene.add(track);

再画点星星点缀一下。


       //buffer做星星
   var bufferGeometry = new THREE.BufferGeometry();
       var gap = 1000; // 定义星星的最近出现位置

   for ( var i = 0; i < positions.length; i += 3 ) {
       // positions

       //-2gap < x < 2gap
       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);
               /*找出x,y,z中绝对值最大的一个数*/
       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: x, y: y, z: z};

       //如果最大值比n要小(因为要在一个距离之外才出现星星)则赋值为n(-n)
       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;
               //70%星星有颜色
       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 ;
       }

       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();

   //星星的material
   var material = new THREE.PointsMaterial( { size: 6, vertexColors: THREE.VertexColors } );
   var particleSystem = new THREE.Points( bufferGeometry, material );

   scene.add( particleSystem );

这样就形成一个星空环境了。扫二维码看效果。 
下载 (2).png

3. 创造虚拟现实的分屏效果

最后,也就是关键时刻。 
1) DeviceOrientationControls.js,一个three.js插件,它帮助我们完成之前提到过的deviceorientation事件,让摄像头根据设备的移动进行调整,形成上帝视角;

2) OrbitControls.js,这是一个备用的控制器,当设备不支持deviceorientation事件改用鼠标调整摄像头;

3) StereoEffect.js,也一个three.js插件,快速打造虚拟现实分屏效果。

看核心代码

    //绑定deviceorientation事件
   function setOrientationControls(e){
       //事件监听器提供一组alpha,beta和gamma值。如果没有alpha值就不能通过deviceorientation事件进行控制,转而替代的是OrbitControls.js
       if (e.alpha) {
           controls = new THREE.DeviceOrientationControls(camera, true);
           //controls.connect();
           controls.update();
       }
       window.removeEventListener('deviceorientation', setOrientationControls, true);
   }
   window.addEventListener('deviceorientation', setOrientationControls, true);

       //没有alpha值就用鼠标来
       controls = new THREE.OrbitControls(camera, canvas);
   controls.target.set(
       camera.position.x,
       camera.position.y,
       camera.position.z - 0.15
   );
   controls.noPan = true;
   controls.noZoom = true;

   //分屏
   var effect = new THREE.StereoEffect(renderer);
   (function render(timestamp) {
       ...
       //renderer.render(scene, camera);
       effect.render(scene, camera);
   }());

遇到的问题

1) three.js的版本要用78,THREE.StereoEffect才有效果,之前用77版的一直无效。 
2) THREE.ImageUtils.loadTexture 注意图片路径跨域问题


本文由 w3cmark_前端笔记 版权所有,转载时请注明出处。
注明出处格式:w3cmark (http://www.w3cmark.com/2016/threejs-webvr.html)

分享到:

关注w3cmark
微信公众号 w3cmark_com