天天看點

【騰訊Bugly幹貨分享】WebVR如此近-three.js的WebVR示例解析

最近VR的發展十分吸引人們的眼球,很多同學應該也心癢癢的想體驗VR裝置,然而現在的專業硬體價格還比較高,入手一個估計就要吃土了。但是,對于我們前端開發者來說,我們不僅可以簡單地在手機上進行視覺上的VR體驗,還可以立馬上手進行Web端VR應用的開發!

本文來自于騰訊bugly開發者社群,非經作者同意,請勿轉載,原文位址:http://dev.qq.com/topic/57c7ff1689a6c9121b1adb16

作者:蘇晏烨

關于WebVR

WebVR是一個實驗性的Javascript API,允許HMD(head-mounted displays)連接配接到web apps,同時能夠接受這些裝置的位置和動作資訊。這讓使用Javascript開發VR應用成為可能(當然已經有很多接口API讓Javascript作為開發語言了,不過這并不影響我們為WebVR感到興奮)。而讓我們能夠立馬進行預覽與體驗,移動裝置上的chrome已經支援了WebVR并使手機作為一個簡易的HMD。手機可以把螢幕分成左右眼視覺并應用手機中的加速度計、陀螺儀等感應器,你需要做的或許就隻是買一個cardboard。

不說了,我去下單了!

img cardborad紙盒,一頓食堂飯錢即可入手

前言

WebVR仍處于w3c的草案階段,是以開發和體驗都需要polyfill。

這篇解析基于 webvr-boilerplate ,這個示例的作者,任職google的 Boris Smus 同時也編寫了 webvr-polyfill 。 three.js examples中也提供了關于VR的控制例子。這裡主要通過對代碼注釋的方式來解讀關鍵的檔案。

示例的最終效果如下,打開Demo并把手機放進cardboard即可體驗。你也可以在我的github對照有關的代碼和注釋。

Demo連結:http://soaanyip.github.io/webvr-boilerplate-cn/

我的github:https://github.com/SoAanyip/WebVR-Boilerplate-CN/

按照慣例,這篇解析預設你至少有three.js相關的基礎知識。有興趣也可以浏覽一下我之前寫的

《ThreeJS 輕松實作主視覺太陽系漫遊》

https://zhuanlan.zhihu.com/p/20796329

這篇解析中three.js的版本為V76。文中如有各種錯誤請指出!

先從html開始

在示例中隻用到了一個index.html。首先meta标簽有幾個值得注意的:

<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0, shrink-to-fit=no">
 <meta name="mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-capable" content="yes" />
 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
           

這幾個标簽對web app開發的同學來說應該是十分熟悉了。其中

shrink-to-fit=no

是Safari的特性,禁止頁面通過縮放去适應适口。

接下來在js引用的部分,引用了這幾個資源:

//作者引入的一個promise polyfill;
 <script src="node_modules/es6-promise/dist/es6-promise.js"></script>
           
//three.js核心庫
 <script src="node_modules/three/three.js"></script>
           
//從連接配接的VR裝置中獲得位置資訊并應用在camera對象上,将在下文展開;
 <script src="node_modules/three/examples/js/controls/VRControls.js"></script>
           
//處理立體視覺和繪制相關,将在下文展開;
 <script src="node_modules/three/examples/js/effects/VREffect.js"></script>
           
//WebVR polyfill,下文簡述調用的API option;
 <script src="node_modules/webvr-polyfill/build/webvr-polyfill.js"></script>
           
// 界面按鈕以及進入/退出VR模式的控制等。
 <script src="build/webvr-manager.js"></script>
           

具體的整個項目檔案,可以在這裡檢視有關的代碼和注釋。

VRControls.js - HMD狀态感應

這個檔案主要對HMD的狀态資訊進行擷取并應用到camera上。例如在手機上顯示的時候,手機的旋轉傾斜等就會直接作用到camera上。

第一步是擷取連接配接的VR裝置,這一步是基本通過WebVR的API進行的:

//擷取VR裝置(作為資訊輸入源。如有多個則隻取第一個)
 function gotVRDevices( devices ) {
     for ( var i = 0; i < devices.length; i ++ ) {
         if ( ( 'VRDisplay' in window && devices[ i ] instanceof VRDisplay ) || ( 'PositionSensorVRDevice' in window && devices[ i ] instanceof PositionSensorVRDevice ) ) {
             vrInput = devices[ i ];
             break;  // We keep the first we encounter
         }
     }

     if ( !vrInput ) {
         if ( onError ) onError( 'VR input not available.' );
     }
 }
 //調用WebVR API擷取VR裝置
 if ( navigator.getVRDisplays ) {
     navigator.getVRDisplays().then( gotVRDevices );
 } else if ( navigator.getVRDevices ) {
     // Deprecated API.
     navigator.getVRDevices().then( gotVRDevices );
 }
           

然後是三個關于位置的參數:

// the Rift SDK returns the position in meters
 // this scale factor allows the user to define how meters
 // are converted to scene units.
 //Rift SDK傳回的位置資訊是以米作為機關的。這裡可以定義以幾倍的縮放比例轉換為three.js中的長度。
 this.scale = 1;

 // If true will use "standing space" coordinate system where y=0 is the
 // floor and x=0, z=0 is the center of the room.
 //表示使用者是否站立姿态。當為false時camra會在y=0的位置,而為true時會結合下面的模拟身高來決定camera的y值。
 //在無法擷取使用者姿勢資訊的裝置上,需要在調用時直接指定是站姿還是坐姿。
 this.standing = false;

 // Distance from the users eyes to the floor in meters. Used when
 // standing=true but the VRDisplay doesn't provide stageParameters.
 //當為站立姿态時,使用者的眼睛(camera)的高度(跟如有硬體時傳回的機關一緻,為米)。這裡會受scale的影響。如scale為2時,實際camera的高度就是3.2。
 this.userHeight = 1.6;
           

通過WebVR API擷取到使用者的裝置資訊,并應用到camera上,是一個持續進行的過程。是以這部分的資訊更新會在requestAnimationFrame中不斷地調用。

//将在requestAnimationFrame中應用更新
 this.update = function () {
     if ( vrInput ) {
         if ( vrInput.getPose ) {
             //方法傳回傳感器在某一時刻的資訊(object)。例如包括時間戳、位置(x,y,z)、線速度、線加速度、角速度、角加速度、方向資訊。
             var pose = vrInput.getPose();
             //orientation 方向
             if ( pose.orientation !== null ) {
                 //quaternion  四元數
                 //把裝置的方向複制給camera
                 object.quaternion.fromArray( pose.orientation );
             }
             //位置資訊
             if ( pose.position !== null ) {
                 //同樣把裝置的位置複制給camera
                 object.position.fromArray( pose.position );
             } else {
                 object.position.set( 0, 0, 0 );
             }

         } else {
             // Deprecated API.
             var state = vrInput.getState();
             if ( state.orientation !== null ) {
                 object.quaternion.copy( state.orientation );
             }
             if ( state.position !== null ) {
                 object.position.copy( state.position );
             } else {
                 object.position.set( 0, 0, 0 );
             }
         }

         //TODO 此塊會一直執行
         if ( this.standing ) {
             //如果硬體傳回場景資訊,則應用硬體傳回的資料來進行站姿轉換
             if ( vrInput.stageParameters ) {
                 object.updateMatrix();
                 //sittingToStandingTransform傳回一個Matrix4,表示從坐姿到站姿的變換。
                 standingMatrix.fromArray(vrInput.stageParameters.sittingToStandingTransform);
                 //應用變換到camera。
                 object.applyMatrix( standingMatrix );
             } else {
                 //如果vrInput不提供y高度資訊的話使用userHeight作為高度
                 object.position.setY( object.position.y + this.userHeight );
             }

         }
         //使用上面定義的this.scale來縮放camera的位置。
         object.position.multiplyScalar( scope.scale );
     }
 };
           

以上是vrcontrols的關鍵代碼。

VREffect.js - 立體視覺

VREffect.js主要把螢幕顯示切割為左右眼所視的螢幕,兩個螢幕所顯示的内容具有一定的差異,使得人的雙目立體視覺可以把螢幕中的内容看得立體化。這個檔案主要的流程如下圖:

首先是對畫布大小進行了設定。其中

renderer.setPixelRatio( 1 );

是防止在retina等螢幕上出現圖像變形等顯示問題。

//初始化或者resize的時候進行。
 this.setSize = function ( width, height ) {
     rendererSize = { width: width, height: height };

     //是否VR模式中
     if ( isPresenting ) {
         //getEyeParameters包含了渲染某個眼睛所視的螢幕的資訊,例如offset,FOV等
         var eyeParamsL = vrHMD.getEyeParameters( 'left' );
         //裝置像素比
         //若裝置像素比不為1時會出現顯示問題。
         //https://github.com/mrdoob/three.js/pull/6248
         renderer.setPixelRatio( 1 );

         if ( isDeprecatedAPI ) {
             renderer.setSize( eyeParamsL.renderRect.width * 2, eyeParamsL.renderRect.height, false );

         } else {
             renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false );
         }

     } else {
         renderer.setPixelRatio( rendererPixelRatio );
         renderer.setSize( width, height );
     }
 };
           

然後是關于全屏模式的設定,這裡跟上面的設定差不遠:

//顯示裝置進入全屏顯示模式
 function onFullscreenChange () {
     var wasPresenting = isPresenting;
     isPresenting = vrHMD !== undefined && ( vrHMD.isPresenting || ( isDeprecatedAPI && document[ fullscreenElement ] instanceof window.HTMLElement ) );
     if ( wasPresenting === isPresenting ) {
         return;
     }

     //如果此次事件是進入VR模式
     if ( isPresenting ) {
         rendererPixelRatio = renderer.getPixelRatio();
         rendererSize = renderer.getSize();

         //getEyeParameters包含了渲染某個眼睛所視的螢幕的資訊,例如offset,FOV等
         var eyeParamsL = vrHMD.getEyeParameters( 'left' );
         var eyeWidth, eyeHeight;

         if ( isDeprecatedAPI ) {
             eyeWidth = eyeParamsL.renderRect.width;
             eyeHeight = eyeParamsL.renderRect.height;
         } else {
             eyeWidth = eyeParamsL.renderWidth;
             eyeHeight = eyeParamsL.renderHeight;
         }
         renderer.setPixelRatio( 1 );
         renderer.setSize( eyeWidth * 2, eyeHeight, false );

     } else {
         renderer.setPixelRatio( rendererPixelRatio );
         renderer.setSize( rendererSize.width, rendererSize.height );
     }
 }
           

接下來是對表示左右眼的camera的設定。兩個camera也肯定是PerspectiveCamera:

var cameraL = new THREE.PerspectiveCamera();
 //左camera顯示layer 1層(即當某個元素隻出現在layer 1時,隻有cameraL可見。)
 cameraL.layers.enable( 1 );

 var cameraR = new THREE.PerspectiveCamera();
 cameraR.layers.enable( 2 );
           

從WebVR API中擷取關于某個眼睛所視的螢幕的資訊:

//getEyeParameters包含了渲染某個眼睛所視的螢幕的資訊,例如offset,FOV等
 var eyeParamsL = vrHMD.getEyeParameters( 'left' );
 var eyeParamsR = vrHMD.getEyeParameters( 'right' );

 if ( ! isDeprecatedAPI ) {
     // represents the offset from the center point between the user's eyes to the center of the eye, measured in meters.
     //瞳距的偏移
     eyeTranslationL.fromArray( eyeParamsL.offset );
     eyeTranslationR.fromArray( eyeParamsR.offset );
     //represents a field of view defined by 4 different degree values describing the view from a center point.
     //獲得左右眼的FOV
     eyeFOVL = eyeParamsL.fieldOfView;
     eyeFOVR = eyeParamsR.fieldOfView;

 } else {
     eyeTranslationL.copy( eyeParamsL.eyeTranslation );
     eyeTranslationR.copy( eyeParamsR.eyeTranslation );
     eyeFOVL = eyeParamsL.recommendedFieldOfView;
     eyeFOVR = eyeParamsR.recommendedFieldOfView;
 }

 if ( Array.isArray( scene ) ) {
     console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' );
     scene = scene[ 0 ];
 }
           

由于左右camera的視錐體還沒确定,需要對獲得的FOV資訊進行計算來确定。在涉及透視投影矩陣的部分會比較複雜,是以這裡不展開來說。如果有錯誤請指出:

cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
 cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );

 //角度弧度的轉換,然後進行後續的計算
 function fovToProjection( fov, rightHanded, zNear, zFar ) {
     //角度轉換為弧度  如30度轉為1/6 PI
     var DEG2RAD = Math.PI / 180.0;

     var fovPort = {
         upTan: Math.tan( fov.upDegrees * DEG2RAD ),
         downTan: Math.tan( fov.downDegrees * DEG2RAD ),
         leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
         rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
     };

     return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
 }

 //根據從裝置獲得的FOV以及相機設定的near、far來生成透視投影矩陣
 function fovPortToProjection( fov, rightHanded, zNear, zFar ) {

     //使用右手坐标
     rightHanded = rightHanded === undefined ? true : rightHanded;
     zNear = zNear === undefined ? 0.01 : zNear;
     zFar = zFar === undefined ? 10000.0 : zFar;

     var handednessScale = rightHanded ? - 1.0 : 1.0;

     // start with an identity matrix

     var mobj = new THREE.Matrix4();
     var m = mobj.elements;

     // and with scale/offset info for normalized device coords
     var scaleAndOffset = fovToNDCScaleOffset( fov );

     //建立透視投影矩陣

     // X result, map clip edges to [-w,+w]
     m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
     m[ 0 * 4 + 1 ] = 0.0;
     m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
     m[ 0 * 4 + 3 ] = 0.0;

     // Y result, map clip edges to [-w,+w]
     // Y offset is negated because this proj matrix transforms from world coords with Y=up,
     // but the NDC scaling has Y=down (thanks D3D?)
     //NDC(歸一化裝置坐标系)是左手坐标系
     m[ 1 * 4 + 0 ] = 0.0;
     m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
     m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
     m[ 1 * 4 + 3 ] = 0.0;

     // Z result (up to the app)
     m[ 2 * 4 + 0 ] = 0.0;
     m[ 2 * 4 + 1 ] = 0.0;
     m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
     m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );

     // W result (= Z in)
     m[ 3 * 4 + 0 ] = 0.0;
     m[ 3 * 4 + 1 ] = 0.0;
     m[ 3 * 4 + 2 ] = handednessScale;
     m[ 3 * 4 + 3 ] = 0.0;

     //轉置矩陣,因為mobj.elements是column-major的
     mobj.transpose();

     return mobj;
 }

 //計算線性插值資訊
 function fovToNDCScaleOffset( fov ) {

     var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
     var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
     var pyscale = 2.0 / ( fov.upTan + fov.downTan );
     var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
     return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
 }
           

之後是确定左右camera的位置和方向。由于左右眼(左右camera)肯定是在頭部(主camera,位置和方向由HMD傳回的資訊确定)上的,在我們獲得把眼睛從頭部飛出去的超能力之前,左右camera的位置和方向都是根據主camera來設定的。

//使主camera的位移、旋轉、縮放變換分解,作用到左camra 右camera上。
 camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
 camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );

 var scale = this.scale;
 //左右眼camera根據瞳距進行位移。
 cameraL.translateOnAxis( eyeTranslationL, scale );
 cameraR.translateOnAxis( eyeTranslationR, scale );
           

最後便是對兩個區域進行渲染。

// 渲染左眼視覺
 renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
 renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
 renderer.render( scene, cameraL );

 // 渲染右眼視覺
 renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
 renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
 renderer.render( scene, cameraR );
           

VREffect檔案的關鍵點差不多是上述這些。

webvr-polyfill.js - 讓現在使用WebVR成為可能

webvr-polyfill.js

根據WebVR API的草案來實作了一套polyfill。例如根據所處環境是pc還是手機來确定使用的是

CardboardVRDisplay

還是

MouseKeyboardVRDisplay

,在手機環境下的話使用

Device API

來處理手機旋轉、方向等參數的擷取。此外作者還順便做了幾個提示圖示和畫面來優化體驗。在這裡我們來看一下其API參數:

WebVRConfig = {
   /**
    * webvr-polyfill configuration
    */

   // Flag to disabled the UI in VR Mode.
   //是否禁用VR模式的UI。
   CARDBOARD_UI_DISABLED: false, // Default: false

   // Forces availability of VR mode.
   //是否強制使VR模式可用。
   //FORCE_ENABLE_VR: true, // Default: false.

   // Complementary filter coefficient. 0 for accelerometer, 1 for gyro.
   //互補濾波系數。加速度計在靜止的時候是很準的,但運動時的角度噪聲很大,陀螺儀反之。
   //互補濾波器徘徊在信任陀螺儀和加速度計的邊界。首先選擇一個時間常數,然後用它來計算濾波器系數。
   //例如陀螺儀的漂移是每秒2度,則可能需要一個少于一秒的時間常數去保證在每一個方向上的漂移不會超過2度。
   //但是當時間常數越低,越多加速度計的噪聲将允許通過。是以這是一個權衡的内容。
   //K_FILTER: 0.98, // Default: 0.98.

   // Flag to disable the instructions to rotate your device.
   //是否禁用旋轉裝置的提示(橫放手機以進入全屏)。
   ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false

   // How far into the future to predict during fast motion.
   //由于有給定的方向以及陀螺儀資訊,選擇允許預測多長時間之内的裝置方向,在裝置快速移動的情況下可以讓渲染比較流暢。
   //PREDICTION_TIME_S: 0.040, // Default: 0.040 (in seconds).

   // Flag to disable touch panner. In case you have your own touch controls、
   //是否禁用提供的觸摸控制,當你有自己的觸摸控制方式時可以禁用
   //TOUCH_PANNER_DISABLED: true, // Default: false.

   // To disable keyboard and mouse controls, if you want to use your own
   // implementation.
   //是否禁用pc下的滑鼠、鍵盤控制。同上。
   //MOUSE_KEYBOARD_CONTROLS_DISABLED: true, // Default: false.

   // Enable yaw panning only, disabling roll and pitch. This can be useful for
   // panoramas with nothing interesting above or below.
   // 僅關心左右角度變化,忽略上下和傾斜等。
   // YAW_ONLY: true, // Default: false.

   // Prevent the polyfill from initializing immediately. Requires the app
   // to call InitializeWebVRPolyfill() before it can be used.
   //是否阻止元件直接進行初始化建構。如果為true則需要自己調用InitializeWebVRPolyfill()。
   //DEFER_INITIALIZATION: true, // Default: false.

   // Enable the deprecated version of the API (navigator.getVRDevices).
   //允許使用過時版本的API。
   //ENABLE_DEPRECATED_API: true, // Default: false.

   // Scales the recommended buffer size reported by WebVR, which can improve
   // performance. Making this very small can lower the effective resolution of
   // your scene.
   //在VR顯示模式下對WebVR推薦的螢幕比例進行縮放。在IOS下如果不為0.5會出現顯示問題,檢視
   //https://github.com/borismus/webvr-polyfill/pull/106
   BUFFER_SCALE: 0.5, // default: 1.0

   // Allow VRDisplay.submitFrame to change gl bindings, which is more
   // efficient if the application code will re-bind it's resources on the
   // next frame anyway.
   // Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM,
   // gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
   // and gl.TEXTURE_BINDING_2D for texture unit 0
   // Warning: enabling this might lead to rendering issues.
   //允許 VRDisplay.submitFrame使用髒矩形渲染。但是開啟此特性可能會出現渲染問題。
   //DIRTY_SUBMIT_FRAME_BINDINGS: true // default: false
 };
           

其config主要是對一些使用者可選項進行設定。在檔案内部,更多的是對

Device API

的應用等等。

現在就開始編寫WebVR應用吧!

在示例的最後是一個顯示簡單的旋轉立方體的demo。此處可以幫助我們學習怎麼建立一個WebVR應用。

首先是建立好scene、renderer、camera的三要素:

// Setup three.js WebGL renderer. Note: Antialiasing is a big performance hit.
 // Only enable it if you actually need to.
 var renderer = new THREE.WebGLRenderer({antialias: true});
 renderer.setPixelRatio(window.devicePixelRatio);

 // Append the canvas element created by the renderer to document body element.
 document.body.appendChild(renderer.domElement);

 // Create a three.js scene.
 var scene = new THREE.Scene();

 // Create a three.js camera.
 var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);
           

對上面解析過的controls、effect進行調用:

// Apply VR headset positional data to camera.
 var controls = new THREE.VRControls(camera);
 //站立姿态
 controls.standing = true;

 // Apply VR stereo rendering to renderer.
 var effect = new THREE.VREffect(renderer);
 effect.setSize(window.innerWidth, window.innerHeight);

 // Create a VR manager helper to enter and exit VR mode.
 //按鈕和全屏模式管理
 var params = {
   hideButton: false, // Default: false.
   isUndistorted: false // Default: false.
 };
 var manager = new WebVRManager(renderer, effect, params);
           

在場景中,添加一個網格顯示的空間,在空間内加入一個小立方體:

// Add a repeating grid as a skybox.
 var boxSize = 5;
 var loader = new THREE.TextureLoader();
 loader.load('img/box.png', onTextureLoaded);

 function onTextureLoaded(texture) {
   texture.wrapS = THREE.RepeatWrapping;
   texture.wrapT = THREE.RepeatWrapping;
   texture.repeat.set(boxSize, boxSize);

   var geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize);
   var material = new THREE.MeshBasicMaterial({
     map: texture,
     color: 0x01BE00,
     side: THREE.BackSide
   });

   // Align the skybox to the floor (which is at y=0).
   skybox = new THREE.Mesh(geometry, material);
   skybox.position.y = boxSize/2;
   scene.add(skybox);

   // For high end VR devices like Vive and Oculus, take into account the stage
   // parameters provided.
   //在高端的裝置上,要考慮到裝置提供的場景資訊的更新。
   setupStage();
 }

 // Create 3D objects.
 var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
 var material = new THREE.MeshNormalMaterial();
 var cube = new THREE.Mesh(geometry, material);

 // Position cube mesh to be right in front of you.
 cube.position.set(0, controls.userHeight, -1);

 scene.add(cube);
           

最後便是設定requestAnimationFrame的更新。在animate的函數中,不但要考慮立方體的旋轉問題,更重要的是要不斷地擷取HMD傳回的資訊以及對camera進行更新。

// Request animation frame loop function
 var lastRender = 0;
 function animate(timestamp) {
   var delta = Math.min(timestamp - lastRender, 500);
   lastRender = timestamp;

   //立方體的旋轉
   cube.rotation.y += delta * 0.0006;

   // Update VR headset position and apply to camera.
   //更新擷取HMD的資訊
   controls.update();

   // Render the scene through the manager.
   //進行camera更新和場景繪制
   manager.render(scene, camera, timestamp);

   requestAnimationFrame(animate);
 }
           

總結

以上便是此示例的各個檔案的解析。我相信VR的形式除了在遊戲上的應用的前景,在其他方面也有其值得探索的可行性。是以讓我們一起來開始WebVR之旅吧!

參考内容

webvr.info

WebVR_API

http://threejs.org/docs/

https://github.com/borismus/webvr-polyfill

https://github.com/borismus/webvr-boilerplate

https://w3c.github.io/webvr/

Eye FOV

http://blog.csdn.net/popy007/article/category/640562

http://blog.csdn.net/iispring/article/details/27970937

http://www.idom.me/articles/841.html

更多精彩内容歡迎關注bugly的微信公衆賬号:

騰訊 Bugly是一款專為移動開發者打造的品質監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智能合并功能幫助開發同學把每天上報的數千條 Crash 根據根因合并分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的代碼行,實時上報可以在釋出後快速的了解應用的品質情況,适配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!

繼續閱讀