【テンセントBugly干物分かち合い】WebVRがこのように近い-three.jsのWebVR例の解析

40310 ワード

本文はテンセント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トレイ、1食の食堂の食事代は手に入れることができます
前言
WebVRはまだw 3 cの草案段階にあるので、開発も体験も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のバージョンはV 76である.文の中にいろいろな間違いがあれば指摘してください.
まずhtmlから
例ではindex.htmlが1つしか使用されませんでした.まず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 );
}

次に、位置に関する3つのパラメータがあります.
// 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=0true              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;

WebVRAPIを介してユーザのデバイス情報を取得し、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は主にスクリーン表示を左右の目で見るスクリーンにカットし、2つのスクリーンに表示される内容に一定の違いがあり、人の両目立体視がスクリーンの中の内容を立体化することができる.このファイルの主な流れは次の図のようです.
まずキャンバスの大きさを設定しました.このうち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 );
    }
}

次に、左右の目を表すカメラの設定です.2つの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 );

WebVRAPIから、ある目で見た画面に関する情報を取得します.
//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 ] };
}

その後は左右のカメラの位置と方向を決めます.左右の目(左右のカメラ)は必ず頭部(メインカメラ、位置と方向がHMDで返される情報で決まる)にあるので、頭部から目を飛ばす超能力を得る前に、左右のカメラの位置と方向はメインカメラによって設定されています.
//  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 );

最後に、2つの領域をレンダリングします.
//       
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はWebVRAPIの草案に基づいて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の3つの要素を確立します.
// 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公式オペレーティングシステムに適している.ガチョウ工場のエンジニアが使っているから、早く私たちに参加してください.