VR大潮来袭 ---前端开发能做些什么

WebVR未来新潮

WebVR即web + VR的体验方式,我们可以戴着头显享受沉浸式的网页,新的API标准让我们可以使用js语言来开发。本文将介绍如何快速开发一个WebVR网页,在此之前,我们有必要了解WebVR的体验方式。

WebVR体验模式


体验WebVR的方式

WebVR的体验方式可以分为VR模式和裸眼模式

VR模式

1.Mobile VR

如使用cardboard眼镜来体验手机浏览器的webVR网页,浏览器将根据水平陀螺仪的参数来获取用户的头部倾斜和转动的朝向,并告知页面需要渲染哪一个朝向的场景。

2.PC VR

通过佩戴Oculus Rift的分离式头显浏览连接在PC主机端的网页,现支持WebVR API的浏览器主要是火狐的 Firefox Nightly和设置VR enabled的谷歌chrome beta。

裸眼模式

除了VR模式下的体验方式,这里还考虑了裸眼下的体验浏览网页的方式,在PC端如果探测的用户选择进入VR模式,应让用户可以使用鼠标拖拽场景,而在智能手机上则应让用户可以使用touchmove或旋转倾斜手机的方式来改变场景视角。
WebVR的概念大概就如此,这次我们将采用cardboard + mobile的方式来测试我们的WebVR场景,现在踏上我们的开发之旅。

准备工作


测试工具:智能手机 + cardboard式头显 + chrome beta 60+(需开启WebVR选项)

如果你练就了裸眼就能将手机双屏画面看成单屏的能力也可以省下头显。

技术和框架:three.js for WebGL

Three.js是构建3d场景的框架,它封装了WebGL函数,简化了创建场景的代码成本,利用three.js我们可以更优雅地创建出三维场景和三维动画,这里我使用的是0.86版本。
如果想了解纯WebGL开发WebVR应用以及WebVR具体环境配置,可以参考 webvr教程--深度剖析

需要引入的js插件:
1.three.min.js
2.webvr-polyfill.js 由于WebVR API还没被各大主流浏览器支持,因此需要引入它来解决兼容性问题。

3D场景构建


首先我们创建一个HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
    <title>webVR-helloworld</title>
    <style type="text/css">
    * {
        margin: 0;
        padding: 0;
    }
    html,body {
        height: 100%;
        overflow: hidden;
    }
    </style>
</head>
<body>
</body>
<script src="./vendor/three.min.js"></script>
<script src="./vendor/webvr-polyfill.js"></script>
<script></script>
</html>

接下来编写js脚本,开始创建我们的3d场景。

1.创建场景

Three.js中的scene场景是绘制我们3d对象的整个容器

var scene = new THREE.Scene();

2.添加相机

Three.js的相机

Three.js中的camera相机代表用户的眼睛,我们通过设置FOV确定视野范围,

//定义一个60°的视角,视线范围在1到1000的透视相机
var camera = new THREE. new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000);
scene.add(camera);

3.添加渲染器

Three.js的渲染器用来渲染camera所看到的画面

//初始化渲染器 antialias参数为ture表示开启抗锯齿策略
var renderer = new THREE.WebGLRenderer({ antialias: true } );
//设置渲染器渲染尺寸
renderer.setSize(window.innerWidth,window.innerHeight);
//设置渲染背景为白色
renderer.setClearColor(0xeeeeee);
//将渲染场景的canvas放入body标签里
document.body.appendChild(renderer.domElement);
4.添加一个立方体网格
// 创建立方体
var geometry = new THREE.CubeGeometry( 10,10,10);
var material = new THREE.MeshLambertMaterial( { color: 0xef6500,needsUpdate: true,opacity:1,transparent:true} );
var cube = new THREE.Mesh( geometry, material );
cube.position.set(0,100,-50);
cube.rotation.set(Math.PI/6,Math.PI/4,0);
scene.add(cube);

5.启动动画

动画渲染的原理:渲染器的持续调用绘制方法,方法里动态改变物体的属性。
旧版的three.js需要手动调用requestAnimationFrame()方法递归的方式来渲染动画,新版three.js已经封装了该属性,因此只需要通过渲染器renderer.animate(callback)

function update() {
    //让立方体旋转
    cube.rotation.y += 0.01;
    //渲染器渲染场景,等同于给相机按下快门
    renderer.render(scene, camera);
}
renderer.animate(update);//启动动画
基本的3d场景

至此,我们已经绘制了一个简单的3d场景并且让它动了起来,接下来,我们需要让我们的场景可以支持WebVR模式。

WebVR场景开发


WebVR网页开发的基本原理是通过WebVR API获取VR动态数据(VR Display frameData),渲染器根据VR数据来分别绘制左右屏场景,具体步骤如下:

  1. 使用navigator.getVRDisplays获取vr设备示例
    WebVR网页分屏

    vrdisplay是vr设备的实例,我们需要将它传给当前运行的renderer渲染器。
function initVR(renderer) {
    renderer.vr.enabled = true;
    navigator.getVRDisplays().then( function(display) {
        renderer.vr.setDevice(display[0]);
        const button = document.querySelector('.vr-btn');
        VRbutton(display[0],renderer,button,function() {
            button.textContent = '退出VR';
        },function() {
            button.textContent = '进入VR';
        });
    }).catch(err => console.warn(err));
}

这里需要通过按钮来控制当前的渲染模式:

  1. 当点击按钮时,根据display.isPresenting判断当前是否是使用vr设备下进行渲染,如果false,进入2,否则true进入3
  2. 当前非VR模式,点击按钮进入VR模式,此时调用display.requestPresent()display.isPresenting被设置为true,触发window的vrdisplaypresentchange事件
  3. 当前为VR模式,点击按钮退出模式,此时调用display.exitPresent()display.isPresenting被设置为false,触发window的vrdisplaypresentchange事件
/**  VR按钮控制
  * @param {VRDisplay} display VRDisplay实例
  * @param {THREE.WebGLRenderer} renderer 渲染器
  * @param {HTMLElement} button VR控制按钮
  * @param {Function} enterVR 点击进入VR模式时回调
  * @param {Function} exitVR 点击退出VR模式时回调
**/
function VRbutton(display,renderer,button,enterVR,exitVR) {
    if ( display ) {
        button.addEventListener('click', function() {
            // 点击vr按钮控制`isPresenting`状态
            display.isPresenting ? display.exitPresent() : display.requestPresent( [ { source: renderer.domElement } ] );
        });

        window.addEventListener( 'vrdisplaypresentchange', function() {
            // 是否处于vr体验模式中,是则触发enterVR,否则触发exitVR
            display.isPresenting ? enterVR() : exitVR();
        }, false );

    } else {
        // 找不到vr设备实例,则移除按钮
        button.remove();
    }
}

我们可以在vrdisplaypresentchange事件中根据isPresenting的值来改变按钮的UI,而three.js将根据isPresenting的值来决定是常规渲染还是vr模式渲染,在vr模式下,three.js将创建两个camera进行渲染。

代码优化

最后,将WebVR应用写成ES6 class,后面开发流程将按如下图结构来规范代码:

WebVRApp类

第一步,构造函数先初始化VR场景、相机和渲染器;
第二步,在渲染之前调用start方法,在start方法里我们为场景创建3d物体;
最后,调起renderer.animate(this.update)开启动画渲染,update方法里我们可动态操作物体属性,具体代码如下:

class WebVRApp {
  constructor() {
    // 初始化场景
    this.scene = new THREE.Scene();
    // 初始化相机
    this.camera = new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,0.1,1000);
    this.scene.add(this.camera);

    // 初始化渲染器
    this.renderer = new THREE.WebGLRenderer({ antialias: true } );
    this.renderer.setSize(window.innerWidth,window.innerHeight);
    this.renderer.setClearColor(0x519EcB);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    document.querySelector('.main-page').appendChild(this.renderer.domElement);

    this.clock = new THREE.Clock();
    // VR初始化
    this._initVR();
    // 往场景添加3d物体
    this.start();
    // 窗口大小调整监听
    window.addEventListener( 'resize', this._resize.bind(this), false );
    // 渲染动画
    this.renderer.animate(this.update.bind(this));
  }
  // 创建3d物体
  start() {
    const { scene, camera } = this;
    // 创建光线、地面等
    ...
    // 创建立方体
    const geometry = new THREE.CubeGeometry(2, 2, 2);
    const material = new THREE.MeshLambertMaterial({ 
      color: 0xef6500,
    });
    this.cube = new THREE.Mesh( geometry, material );
    this.cube.position.set({ x: 0, y: 0, z: -4 });
    scene.add(this.cube);
  }
  // 动画更新
  update() {
    const {scene,camera,renderer,clock} = this;
    const delta = clock.getDelta() * 60;
    // 启动渲染
    this.cube.rotation.y += 0.1 * delta;
    renderer.render(scene, camera);
  }
  // VR模式初始化
  _initVR() {
    const { renderer } = this;
    renderer.vr.enabled = true;
    // 获取VRDisplay实例
    navigator.getVRDisplays().then( display => {
    // 将display实例传给renderer渲染器
    renderer.vr.setDevice(display[0]);
    const button = document.querySelector('.vr-btn');
    VRButton.init(display[0],renderer,button,() => button.textContent = '退出VR',() => button.textContent = '进入VR');
    }).catch(err => console.warn(err));
  }
  // 窗口调整监听
  _resize() {
    const { camera, renderer } = this;
    // 窗口调整重新调整渲染器
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  }
}
new WebVRApp();
demo示例

完整代码:github.com/YoneChen/WebVR-helloworld

结语


目前,国外的谷歌、火狐、Facebook和国内百度已推出支持WebVR浏览器的版本,微软也宣布将推出自己的VR浏览器,随着后期5g网络极速时代的到来以及HMD头显的价格和平台的成熟,WebVR的体验方式将是革命性的,用户通过WebVR浏览网上商店,线上教学可进行“面对面”师生交流等,基于这种种应用场景,我们可以找到一个更好的动力去学习WebVR。

参考链接


responisve WebVR: 探讨WebVR在不同头显(HMD)的适配方案
MolizaVR example: 火狐WebVR示例
webvr-boilerplate: A starting point for web-based VR experiences that work on all VR headsets.
how to build webvr: How to Build VR on the Web Today

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容