WebVR大潮來襲時,前端開發能做些什么?

WebVR即web + VR的體驗方式,我們可以戴着頭顯享受沉浸式的網頁,新的API標准讓我們可以使用js語言來開發。本文將介紹如何快速開發一個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中的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場景並且讓它動了起來,接下來,我們需要讓我們的場景可以支持WebVR模式。
WebVR場景開發
WebVR網頁開發的基本原理是通過WebVR API獲取VR動態數據(VR Display frameData),渲染器根據VR數據來分別繪制左右屏場景,具體步驟如下:
- 使用
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));
}
這里需要通過按鈕來控制當前的渲染模式:
- 當點擊按鈕時,根據
display.isPresenting
判斷當前是否是使用vr設備下進行渲染,如果false,進入2,否則true進入3 - 當前非VR模式,點擊按鈕進入VR模式,此時調用
display.requestPresent()
,display.isPresenting
被設置為true,觸發window的vrdisplaypresentchange
事件 - 當前為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,后面開發流程將按如下圖結構來規范代碼:

第一步,構造函數先初始化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();

完整代碼: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
作者 | YoneChen
來源 | https://www.jianshu.com/p/c9c03e14ba9d