WebVR即web + VR的体验方式,咱们能够戴着头显享受沉浸式的网页,新的API标准让咱们可使用js语言来开发。本文将介绍如何快速开发一个WebVR网页,在此以前,咱们有必要了解WebVR的体验方式。css
WebVR的体验方式能够分为VR模式和裸眼模式html
1.Mobile VRgit
如使用cardboard眼镜来体验手机浏览器的webVR网页,浏览器将根据水平陀螺仪的参数来获取用户的头部倾斜和转动的朝向,并告知页面须要渲染哪个朝向的场景。github
2.PC VRweb
经过佩戴Oculus Rift的分离式头显浏览链接在PC主机端的网页,现支持WebVR API的浏览器主要是火狐的 Firefox Nightly和设置VR enabled的谷歌chrome beta。chrome
除了VR模式下的体验方式,这里还考虑了裸眼下的体验浏览网页的方式,在PC端若是探测的用户选择进入VR模式,应让用户可使用鼠标拖拽场景,而在智能手机上则应让用户可使用touchmove或旋转倾斜手机的方式来改变场景视角。
WebVR的概念大概就如此,此次咱们将采用cardboard + mobile的方式来测试咱们的WebVR场景,如今踏上咱们的开发之旅。canvas
测试工具:智能手机 + cardboard式头显 + chrome beta 60+(需开启WebVR选项)浏览器
若是你练就了裸眼就能将手机双屏画面当作单屏的能力也能够省下头显。网络
技术和框架:three.js for WebGLapp
Three.js是构建3d场景的框架,它封装了WebGL函数,简化了建立场景的代码成本,利用three.js咱们能够更优雅地建立出三维场景和三维动画,这里我使用的是0.86版本。
若是想了解纯WebGL开发WebVR应用以及WebVR具体环境配置,能够参考 webvr教程--深度剖析。
须要引入的js插件:
1.three.min.js
2.webvr-polyfill.js 因为WebVR API还没被各大主流浏览器支持,所以须要引入它来解决兼容性问题。
首先咱们建立一个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场景。
Three.js中的scene场景是绘制咱们3d对象的整个容器
var scene = new THREE.Scene();
Three.js中的camera相机表明用户的眼睛,咱们经过设置FOV肯定视野范围,
//定义一个60°的视角,视线范围在1到1000的透视相机 var camera = new THREE. new THREE.PerspectiveCamera(60,window.innerWidth/window.innerHeight,1,1000); scene.add(camera);
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);
// 建立立方体 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);
动画渲染的原理:渲染器的持续调用绘制方法,方法里动态改变物体的属性。
旧版的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 API获取VR动态数据(VR Display frameData),渲染器根据VR数据来分别绘制左右屏场景,具体步骤以下:
navigator.getVRDisplays
获取vr设备示例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进入3display.requestPresent()
,display.isPresenting
被设置为true,触发window的vrdisplaypresentchange
事件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; // 建立光线 scene.add(new THREE.AmbientLight(0xFFFFFF)); this.addLight(); // 建立地面 this.addGround(1000,1000); // 建立立方体 this.addCube(2,2,2, 2,-1,-3); } // 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); } addCube(width=2,height=2,depth=2, posX,posY,posZ) { const {scene} = this; // 建立立方体 const geometry = new THREE.CubeGeometry(width,height,depth); const material = new THREE.MeshLambertMaterial({ color: 0xef6500, needsUpdate: true, opacity:1, transparent:true }); const cube = new THREE.Mesh( geometry, material ); cube.position.set({ x: posX, y: posY, z: posZ }); this.cube = cube; scene.add(cube); } addGround(width,height) { const {scene} = this; // 建立地平面 const geometry = new THREE.PlaneBufferGeometry( width, height ); const material = new THREE.MeshPhongMaterial( { color: 0xaaaaaa } ); const ground = new THREE.Mesh( geometry, material ); ground.rotation.x = - Math.PI / 2; ground.position.y = -10; scene.add(cube); } // 动画更新 update() { const {scene,camera,renderer,clock} = this; const delta = clock.getDelta() * 60; // 启动渲染 this.cube.rotation.y += 0.1 * delta; renderer.render(scene, camera); } } 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