做有态度的前端团队

网易FEG前端团队

吱吱说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 注意图片路径跨域问题

手机阅读请扫描下方二维码:

已有 1 条评论

  1. 牛逼

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif