本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57c7ff1689a6c9121b1adb16

作者:苏晏烨

关于WebVR

最近VR的发展十分吸引人们的眼球,很多同学应该也心痒痒的想体验VR设备,然而现在的专业硬件价格还比较高,入手一个估计就要吃土了。但是,对于我们前端开发者来说,我们不仅可以简单地在手机上进行视觉上的VR体验,还可以立马上手进行Web端VR应用的开发!

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-polyfillthree.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标签有几个值得注意的:

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

这几个标签对web app开发的同学来说应该是十分熟悉了。其中 shrink-to-fit=no 是Safari的特性,禁止页面通过缩放去适应适口。

接下来在js引用的部分,引用了这几个资源:

  1. //作者引入的一个promise polyfill;
  2. <script src="node_modules/es6-promise/dist/es6-promise.js"></script>
  1. //three.js核心库
  2. <script src="node_modules/three/three.js"></script>
  1. //从连接的VR设备中获得位置信息并应用在camera对象上,将在下文展开;
  2. <script src="node_modules/three/examples/js/controls/VRControls.js"></script>
  1. //处理立体视觉和绘制相关,将在下文展开;
  2. <script src="node_modules/three/examples/js/effects/VREffect.js"></script>
  1. //WebVR polyfill,下文简述调用的API option;
  2. <script src="node_modules/webvr-polyfill/build/webvr-polyfill.js"></script>
  1. // 界面按钮以及进入/退出VR模式的控制等。
  2. <script src="build/webvr-manager.js"></script>

具体的整个项目文件,可以在这里查看有关的代码和注释。

VRControls.js - HMD状态感应

这个文件主要对HMD的状态信息进行获取并应用到camera上。例如在手机上显示的时候,手机的旋转倾斜等就会直接作用到camera上。

第一步是获取连接的VR设备,这一步是基本通过WebVR的API进行的:

  1. //获取VR设备(作为信息输入源。如有多个则只取第一个)
  2. function gotVRDevices( devices ) {
  3. for ( var i = 0; i < devices.length; i ++ ) {
  4. if ( ( 'VRDisplay' in window && devices[ i ] instanceof VRDisplay ) || ( 'PositionSensorVRDevice' in window && devices[ i ] instanceof PositionSensorVRDevice ) ) {
  5. vrInput = devices[ i ];
  6. break; // We keep the first we encounter
  7. }
  8. }
  9. if ( !vrInput ) {
  10. if ( onError ) onError( 'VR input not available.' );
  11. }
  12. }
  13. //调用WebVR API获取VR设备
  14. if ( navigator.getVRDisplays ) {
  15. navigator.getVRDisplays().then( gotVRDevices );
  16. } else if ( navigator.getVRDevices ) {
  17. // Deprecated API.
  18. navigator.getVRDevices().then( gotVRDevices );
  19. }

然后是三个关于位置的参数:

  1. // the Rift SDK returns the position in meters
  2. // this scale factor allows the user to define how meters
  3. // are converted to scene units.
  4. //Rift SDK返回的位置信息是以米作为单位的。这里可以定义以几倍的缩放比例转换为three.js中的长度。
  5. this.scale = 1;
  6. // If true will use "standing space" coordinate system where y=0 is the
  7. // floor and x=0, z=0 is the center of the room.
  8. //表示使用者是否站立姿态。当为false时camra会在y=0的位置,而为true时会结合下面的模拟身高来决定camera的y值。
  9. //在无法获取用户姿势信息的设备上,需要在调用时直接指定是站姿还是坐姿。
  10. this.standing = false;
  11. // Distance from the users eyes to the floor in meters. Used when
  12. // standing=true but the VRDisplay doesn't provide stageParameters.
  13. //当为站立姿态时,用户的眼睛(camera)的高度(跟如有硬件时返回的单位一致,为米)。这里会受scale的影响。如scale为2时,实际camera的高度就是3.2。
  14. this.userHeight = 1.6;

通过WebVR API获取到用户的设备信息,并应用到camera上,是一个持续进行的过程。因此这部分的信息更新会在requestAnimationFrame中不断地调用。

  1. //将在requestAnimationFrame中应用更新
  2. this.update = function () {
  3. if ( vrInput ) {
  4. if ( vrInput.getPose ) {
  5. //方法返回传感器在某一时刻的信息(object)。例如包括时间戳、位置(x,y,z)、线速度、线加速度、角速度、角加速度、方向信息。
  6. var pose = vrInput.getPose();
  7. //orientation 方向
  8. if ( pose.orientation !== null ) {
  9. //quaternion 四元数
  10. //把设备的方向复制给camera
  11. object.quaternion.fromArray( pose.orientation );
  12. }
  13. //位置信息
  14. if ( pose.position !== null ) {
  15. //同样把设备的位置复制给camera
  16. object.position.fromArray( pose.position );
  17. } else {
  18. object.position.set( 0, 0, 0 );
  19. }
  20. } else {
  21. // Deprecated API.
  22. var state = vrInput.getState();
  23. if ( state.orientation !== null ) {
  24. object.quaternion.copy( state.orientation );
  25. }
  26. if ( state.position !== null ) {
  27. object.position.copy( state.position );
  28. } else {
  29. object.position.set( 0, 0, 0 );
  30. }
  31. }
  32. //TODO 此块会一直执行
  33. if ( this.standing ) {
  34. //如果硬件返回场景信息,则应用硬件返回的数据来进行站姿转换
  35. if ( vrInput.stageParameters ) {
  36. object.updateMatrix();
  37. //sittingToStandingTransform返回一个Matrix4,表示从坐姿到站姿的变换。
  38. standingMatrix.fromArray(vrInput.stageParameters.sittingToStandingTransform);
  39. //应用变换到camera。
  40. object.applyMatrix( standingMatrix );
  41. } else {
  42. //如果vrInput不提供y高度信息的话使用userHeight作为高度
  43. object.position.setY( object.position.y + this.userHeight );
  44. }
  45. }
  46. //使用上面定义的this.scale来缩放camera的位置。
  47. object.position.multiplyScalar( scope.scale );
  48. }
  49. };

以上是vrcontrols的关键代码。

VREffect.js - 立体视觉

VREffect.js主要把屏幕显示切割为左右眼所视的屏幕,两个屏幕所显示的内容具有一定的差异,使得人的双目立体视觉可以把屏幕中的内容看得立体化。这个文件主要的流程如下图:

首先是对画布大小进行了设定。其中 renderer.setPixelRatio( 1 ); 是防止在retina等屏幕上出现图像变形等显示问题。

  1. //初始化或者resize的时候进行。
  2. this.setSize = function ( width, height ) {
  3. rendererSize = { width: width, height: height };
  4. //是否VR模式中
  5. if ( isPresenting ) {
  6. //getEyeParameters包含了渲染某个眼睛所视的屏幕的信息,例如offset,FOV等
  7. var eyeParamsL = vrHMD.getEyeParameters( 'left' );
  8. //设备像素比
  9. //若设备像素比不为1时会出现显示问题。
  10. //https://github.com/mrdoob/three.js/pull/6248
  11. renderer.setPixelRatio( 1 );
  12. if ( isDeprecatedAPI ) {
  13. renderer.setSize( eyeParamsL.renderRect.width * 2, eyeParamsL.renderRect.height, false );
  14. } else {
  15. renderer.setSize( eyeParamsL.renderWidth * 2, eyeParamsL.renderHeight, false );
  16. }
  17. } else {
  18. renderer.setPixelRatio( rendererPixelRatio );
  19. renderer.setSize( width, height );
  20. }
  21. };

然后是关于全屏模式的设置,这里跟上面的设定差不远:

  1. //显示设备进入全屏显示模式
  2. function onFullscreenChange () {
  3. var wasPresenting = isPresenting;
  4. isPresenting = vrHMD !== undefined && ( vrHMD.isPresenting || ( isDeprecatedAPI && document[ fullscreenElement ] instanceof window.HTMLElement ) );
  5. if ( wasPresenting === isPresenting ) {
  6. return;
  7. }
  8. //如果此次事件是进入VR模式
  9. if ( isPresenting ) {
  10. rendererPixelRatio = renderer.getPixelRatio();
  11. rendererSize = renderer.getSize();
  12. //getEyeParameters包含了渲染某个眼睛所视的屏幕的信息,例如offset,FOV等
  13. var eyeParamsL = vrHMD.getEyeParameters( 'left' );
  14. var eyeWidth, eyeHeight;
  15. if ( isDeprecatedAPI ) {
  16. eyeWidth = eyeParamsL.renderRect.width;
  17. eyeHeight = eyeParamsL.renderRect.height;
  18. } else {
  19. eyeWidth = eyeParamsL.renderWidth;
  20. eyeHeight = eyeParamsL.renderHeight;
  21. }
  22. renderer.setPixelRatio( 1 );
  23. renderer.setSize( eyeWidth * 2, eyeHeight, false );
  24. } else {
  25. renderer.setPixelRatio( rendererPixelRatio );
  26. renderer.setSize( rendererSize.width, rendererSize.height );
  27. }
  28. }

接下来是对表示左右眼的camera的设定。两个camera也肯定是PerspectiveCamera:

  1. var cameraL = new THREE.PerspectiveCamera();
  2. //左camera显示layer 1层(即当某个元素只出现在layer 1时,只有cameraL可见。)
  3. cameraL.layers.enable( 1 );
  4. var cameraR = new THREE.PerspectiveCamera();
  5. cameraR.layers.enable( 2 );

从WebVR API中获取关于某个眼睛所视的屏幕的信息:

  1. //getEyeParameters包含了渲染某个眼睛所视的屏幕的信息,例如offset,FOV等
  2. var eyeParamsL = vrHMD.getEyeParameters( 'left' );
  3. var eyeParamsR = vrHMD.getEyeParameters( 'right' );
  4. if ( ! isDeprecatedAPI ) {
  5. // represents the offset from the center point between the user's eyes to the center of the eye, measured in meters.
  6. //瞳距的偏移
  7. eyeTranslationL.fromArray( eyeParamsL.offset );
  8. eyeTranslationR.fromArray( eyeParamsR.offset );
  9. //represents a field of view defined by 4 different degree values describing the view from a center point.
  10. //获得左右眼的FOV
  11. eyeFOVL = eyeParamsL.fieldOfView;
  12. eyeFOVR = eyeParamsR.fieldOfView;
  13. } else {
  14. eyeTranslationL.copy( eyeParamsL.eyeTranslation );
  15. eyeTranslationR.copy( eyeParamsR.eyeTranslation );
  16. eyeFOVL = eyeParamsL.recommendedFieldOfView;
  17. eyeFOVR = eyeParamsR.recommendedFieldOfView;
  18. }
  19. if ( Array.isArray( scene ) ) {
  20. console.warn( 'THREE.VREffect.render() no longer supports arrays. Use object.layers instead.' );
  21. scene = scene[ 0 ];
  22. }

由于左右camera的视锥体还没确定,需要对获得的FOV信息进行计算来确定。在涉及透视投影矩阵的部分会比较复杂,所以这里不展开来说。如果有错误请指出:

  1. cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
  2. cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
  3. //角度弧度的转换,然后进行后续的计算
  4. function fovToProjection( fov, rightHanded, zNear, zFar ) {
  5. //角度转换为弧度 如30度转为1/6 PI
  6. var DEG2RAD = Math.PI / 180.0;
  7. var fovPort = {
  8. upTan: Math.tan( fov.upDegrees * DEG2RAD ),
  9. downTan: Math.tan( fov.downDegrees * DEG2RAD ),
  10. leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
  11. rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
  12. };
  13. return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
  14. }
  15. //根据从设备获得的FOV以及相机设定的near、far来生成透视投影矩阵
  16. function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
  17. //使用右手坐标
  18. rightHanded = rightHanded === undefined ? true : rightHanded;
  19. zNear = zNear === undefined ? 0.01 : zNear;
  20. zFar = zFar === undefined ? 10000.0 : zFar;
  21. var handednessScale = rightHanded ? - 1.0 : 1.0;
  22. // start with an identity matrix
  23. var mobj = new THREE.Matrix4();
  24. var m = mobj.elements;
  25. // and with scale/offset info for normalized device coords
  26. var scaleAndOffset = fovToNDCScaleOffset( fov );
  27. //建立透视投影矩阵
  28. // X result, map clip edges to [-w,+w]
  29. m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
  30. m[ 0 * 4 + 1 ] = 0.0;
  31. m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
  32. m[ 0 * 4 + 3 ] = 0.0;
  33. // Y result, map clip edges to [-w,+w]
  34. // Y offset is negated because this proj matrix transforms from world coords with Y=up,
  35. // but the NDC scaling has Y=down (thanks D3D?)
  36. //NDC(归一化设备坐标系)是左手坐标系
  37. m[ 1 * 4 + 0 ] = 0.0;
  38. m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
  39. m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
  40. m[ 1 * 4 + 3 ] = 0.0;
  41. // Z result (up to the app)
  42. m[ 2 * 4 + 0 ] = 0.0;
  43. m[ 2 * 4 + 1 ] = 0.0;
  44. m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
  45. m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );
  46. // W result (= Z in)
  47. m[ 3 * 4 + 0 ] = 0.0;
  48. m[ 3 * 4 + 1 ] = 0.0;
  49. m[ 3 * 4 + 2 ] = handednessScale;
  50. m[ 3 * 4 + 3 ] = 0.0;
  51. //转置矩阵,因为mobj.elements是column-major的
  52. mobj.transpose();
  53. return mobj;
  54. }
  55. //计算线性插值信息
  56. function fovToNDCScaleOffset( fov ) {
  57. var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
  58. var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
  59. var pyscale = 2.0 / ( fov.upTan + fov.downTan );
  60. var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
  61. return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
  62. }

之后是确定左右camera的位置和方向。由于左右眼(左右camera)肯定是在头部(主camera,位置和方向由HMD返回的信息确定)上的,在我们获得把眼睛从头部飞出去的超能力之前,左右camera的位置和方向都是根据主camera来设定的。

  1. //使主camera的位移、旋转、缩放变换分解,作用到左camra 右camera上。
  2. camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
  3. camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
  4. var scale = this.scale;
  5. //左右眼camera根据瞳距进行位移。
  6. cameraL.translateOnAxis( eyeTranslationL, scale );
  7. cameraR.translateOnAxis( eyeTranslationR, scale );

最后便是对两个区域进行渲染。

  1. // 渲染左眼视觉
  2. renderer.setViewport( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
  3. renderer.setScissor( renderRectL.x, renderRectL.y, renderRectL.width, renderRectL.height );
  4. renderer.render( scene, cameraL );
  5. // 渲染右眼视觉
  6. renderer.setViewport( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
  7. renderer.setScissor( renderRectR.x, renderRectR.y, renderRectR.width, renderRectR.height );
  8. renderer.render( scene, cameraR );

VREffect文件的关键点差不多是上述这些。

webvr-polyfill.js - 让现在使用WebVR成为可能

webvr-polyfill.js 根据WebVR API的草案来实现了一套polyfill。例如根据所处环境是pc还是手机来确定使用的是 CardboardVRDisplay 还是 MouseKeyboardVRDisplay ,在手机环境下的话使用 Device API 来处理手机旋转、方向等参数的获取。此外作者还顺便做了几个提示图标和画面来优化体验。在这里我们来看一下其API参数:

  1. WebVRConfig = {
  2. /**
  3. * webvr-polyfill configuration
  4. */
  5. // Flag to disabled the UI in VR Mode.
  6. //是否禁用VR模式的UI。
  7. CARDBOARD_UI_DISABLED: false, // Default: false
  8. // Forces availability of VR mode.
  9. //是否强制使VR模式可用。
  10. //FORCE_ENABLE_VR: true, // Default: false.
  11. // Complementary filter coefficient. 0 for accelerometer, 1 for gyro.
  12. //互补滤波系数。加速度计在静止的时候是很准的,但运动时的角度噪声很大,陀螺仪反之。
  13. //互补滤波器徘徊在信任陀螺仪和加速度计的边界。首先选择一个时间常数,然后用它来计算滤波器系数。
  14. //例如陀螺仪的漂移是每秒2度,则可能需要一个少于一秒的时间常数去保证在每一个方向上的漂移不会超过2度。
  15. //但是当时间常数越低,越多加速度计的噪声将允许通过。所以这是一个权衡的内容。
  16. //K_FILTER: 0.98, // Default: 0.98.
  17. // Flag to disable the instructions to rotate your device.
  18. //是否禁用旋转设备的提示(横放手机以进入全屏)。
  19. ROTATE_INSTRUCTIONS_DISABLED: false, // Default: false
  20. // How far into the future to predict during fast motion.
  21. //由于有给定的方向以及陀螺仪信息,选择允许预测多长时间之内的设备方向,在设备快速移动的情况下可以让渲染比较流畅。
  22. //PREDICTION_TIME_S: 0.040, // Default: 0.040 (in seconds).
  23. // Flag to disable touch panner. In case you have your own touch controls、
  24. //是否禁用提供的触摸控制,当你有自己的触摸控制方式时可以禁用
  25. //TOUCH_PANNER_DISABLED: true, // Default: false.
  26. // To disable keyboard and mouse controls, if you want to use your own
  27. // implementation.
  28. //是否禁用pc下的鼠标、键盘控制。同上。
  29. //MOUSE_KEYBOARD_CONTROLS_DISABLED: true, // Default: false.
  30. // Enable yaw panning only, disabling roll and pitch. This can be useful for
  31. // panoramas with nothing interesting above or below.
  32. // 仅关心左右角度变化,忽略上下和倾斜等。
  33. // YAW_ONLY: true, // Default: false.
  34. // Prevent the polyfill from initializing immediately. Requires the app
  35. // to call InitializeWebVRPolyfill() before it can be used.
  36. //是否阻止组件直接进行初始化构建。如果为true则需要自己调用InitializeWebVRPolyfill()。
  37. //DEFER_INITIALIZATION: true, // Default: false.
  38. // Enable the deprecated version of the API (navigator.getVRDevices).
  39. //允许使用过时版本的API。
  40. //ENABLE_DEPRECATED_API: true, // Default: false.
  41. // Scales the recommended buffer size reported by WebVR, which can improve
  42. // performance. Making this very small can lower the effective resolution of
  43. // your scene.
  44. //在VR显示模式下对WebVR推荐的屏幕比例进行缩放。在IOS下如果不为0.5会出现显示问题,查看
  45. //https://github.com/borismus/webvr-polyfill/pull/106
  46. BUFFER_SCALE: 0.5, // default: 1.0
  47. // Allow VRDisplay.submitFrame to change gl bindings, which is more
  48. // efficient if the application code will re-bind it's resources on the
  49. // next frame anyway.
  50. // Dirty bindings include: gl.FRAMEBUFFER_BINDING, gl.CURRENT_PROGRAM,
  51. // gl.ARRAY_BUFFER_BINDING, gl.ELEMENT_ARRAY_BUFFER_BINDING,
  52. // and gl.TEXTURE_BINDING_2D for texture unit 0
  53. // Warning: enabling this might lead to rendering issues.
  54. //允许 VRDisplay.submitFrame使用脏矩形渲染。但是开启此特性可能会出现渲染问题。
  55. //DIRTY_SUBMIT_FRAME_BINDINGS: true // default: false
  56. };

其config主要是对一些用户可选项进行设定。在文件内部,更多的是对 Device API 的应用等等。

现在就开始编写WebVR应用吧!

在示例的最后是一个显示简单的旋转立方体的demo。此处可以帮助我们学习怎么创建一个WebVR应用。

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

  1. // Setup three.js WebGL renderer. Note: Antialiasing is a big performance hit.
  2. // Only enable it if you actually need to.
  3. var renderer = new THREE.WebGLRenderer({antialias: true});
  4. renderer.setPixelRatio(window.devicePixelRatio);
  5. // Append the canvas element created by the renderer to document body element.
  6. document.body.appendChild(renderer.domElement);
  7. // Create a three.js scene.
  8. var scene = new THREE.Scene();
  9. // Create a three.js camera.
  10. var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000);

对上面解析过的controls、effect进行调用:

  1. // Apply VR headset positional data to camera.
  2. var controls = new THREE.VRControls(camera);
  3. //站立姿态
  4. controls.standing = true;
  5. // Apply VR stereo rendering to renderer.
  6. var effect = new THREE.VREffect(renderer);
  7. effect.setSize(window.innerWidth, window.innerHeight);
  8. // Create a VR manager helper to enter and exit VR mode.
  9. //按钮和全屏模式管理
  10. var params = {
  11. hideButton: false, // Default: false.
  12. isUndistorted: false // Default: false.
  13. };
  14. var manager = new WebVRManager(renderer, effect, params);

在场景中,添加一个网格显示的空间,在空间内加入一个小立方体:

  1. // Add a repeating grid as a skybox.
  2. var boxSize = 5;
  3. var loader = new THREE.TextureLoader();
  4. loader.load('img/box.png', onTextureLoaded);
  5. function onTextureLoaded(texture) {
  6. texture.wrapS = THREE.RepeatWrapping;
  7. texture.wrapT = THREE.RepeatWrapping;
  8. texture.repeat.set(boxSize, boxSize);
  9. var geometry = new THREE.BoxGeometry(boxSize, boxSize, boxSize);
  10. var material = new THREE.MeshBasicMaterial({
  11. map: texture,
  12. color: 0x01BE00,
  13. side: THREE.BackSide
  14. });
  15. // Align the skybox to the floor (which is at y=0).
  16. skybox = new THREE.Mesh(geometry, material);
  17. skybox.position.y = boxSize/2;
  18. scene.add(skybox);
  19. // For high end VR devices like Vive and Oculus, take into account the stage
  20. // parameters provided.
  21. //在高端的设备上,要考虑到设备提供的场景信息的更新。
  22. setupStage();
  23. }
  24. // Create 3D objects.
  25. var geometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
  26. var material = new THREE.MeshNormalMaterial();
  27. var cube = new THREE.Mesh(geometry, material);
  28. // Position cube mesh to be right in front of you.
  29. cube.position.set(0, controls.userHeight, -1);
  30. scene.add(cube);

最后便是设置requestAnimationFrame的更新。在animate的函数中,不但要考虑立方体的旋转问题,更重要的是要不断地获取HMD返回的信息以及对camera进行更新。

  1. // Request animation frame loop function
  2. var lastRender = 0;
  3. function animate(timestamp) {
  4. var delta = Math.min(timestamp - lastRender, 500);
  5. lastRender = timestamp;
  6. //立方体的旋转
  7. cube.rotation.y += delta * 0.0006;
  8. // Update VR headset position and apply to camera.
  9. //更新获取HMD的信息
  10. controls.update();
  11. // Render the scene through the manager.
  12. //进行camera更新和场景绘制
  13. manager.render(scene, camera, timestamp);
  14. requestAnimationFrame(animate);
  15. }

总结

以上便是此示例的各个文件的解析。我相信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 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

【腾讯Bugly干货分享】WebVR如此近-three.js的WebVR示例解析的更多相关文章

  1. 【腾讯Bugly干货分享】React Native项目实战总结

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7 “8小时内拼工作,8小时外拼成长 ...

  2. 【腾讯Bugly干货分享】腾讯验证码的十二年

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581301b146dfb1456904df8d Dev Club 是一个交流移动 ...

  3. 【腾讯Bugly干货分享】移动互联网测试到质量的转变

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ee0934b690d84c3188d7c7 Dev Club 是一个交流移动 ...

  4. 【腾讯Bugly干货分享】聊一聊微信“小程序”

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ecdf5ef03abecd43216fd0 Dev Club 是一个交流移动 ...

  5. 【腾讯Bugly干货分享】深入源码探索 ReactNative 通信机制

    Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 本文从源码角度剖析 RNA 中 J ...

  6. 【腾讯Bugly干货分享】职场中脱颖而出的成长秘诀

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/uQKpVg7HMLfogGzzMyc9iQ 导语 时光 ...

  7. 【腾讯Bugly干货分享】Android性能优化典范——第6季

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/580d91208d80e49771f0a07c 导语 这里是Android性能优 ...

  8. 【腾讯Bugly干货分享】基于RxJava的一种MVP实现

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57bfef673c1174283d60bac0 Dev Club 是一个交流移动 ...

  9. 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...

随机推荐

  1. Elasticsearch之java的基本操作一

    摘要   接触ElasticSearch已经有一段了.在这期间,遇到很多问题,但在最后自己的不断探索下解决了这些问题.看到网上或多或少的都有一些介绍ElasticSearch相关知识的文档,但个人觉得 ...

  2. Word/Excel 在线预览

    前言 近日项目中做到一个功能,需要上传附件后能够在线预览.之前也没做过这类似的,于是乎就查找了相关资料,.net实现Office文件预览大概有这几种方式: ① 使用Microsoft的Office组件 ...

  3. Android性能优化之利用LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来. ...

  4. js学习之类型识别

    用来判别类型的方法有好多,整理了一下4种方法,平时用的时候,在不同情景下,还是要结合着使用的. 方法一 typeof:可以识别标准类型,除了Null:不能识别具体的对象类型,除了Function &l ...

  5. input标签中button在iPhone中圆角的问题

    1.问题 使用H5编写微信页面时,使用<input type="button"/>时,在Android手机中显示正常,但是在iPhone手机中则显示不正常,显示为圆角样 ...

  6. ASP.NET MVC关于Ajax以及Jquery的无限级联动

    ---恢复内容开始--- 第一次发表博文,发表博文的目的是巩固自己的技术,也能够共享给大家.写的不好的地方,希望大家多给给意见.老司机勿喷 数据结构() NewsTypeId 新闻ID, NewsTy ...

  7. 3种web会话管理的方式

    http是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的,但是对于我们的应用来说,我们是靠用户来管理,而不是靠客户端. ...

  8. JavaScript学习笔记(四)——jQuery插件开发与发布

    jQuery插件就是以jQuery库为基础衍生出来的库,jQuery插件的好处是封装功能,提高了代码的复用性,加快了开发速度,现在网络上开源的jQuery插件非常多,随着版本的不停迭代越来越稳定好用, ...

  9. 易用BPM时代,企业如何轻松驾驭H3?

    众所周知,BPM作为企业发展的推动力,能敏捷高效的融合业务流程和信息资源.通过综合考虑流程的成本.效率.质量等方面因素,用IT系统将调整后的流程固化下来,从而降低企业管理成本,提高内部运营效率,提升企 ...

  10. 跨域问题,前端主动向后台发送cookie

    跨域是什么? 从一个域名的网页访问另一个域名的资源,就会出现跨域.只要协议.端口.域名有一个不同就会出现跨域 例如: 1.协议不同  http://www.baidu.com:80 和 https:/ ...