LoginSignup
18
18

More than 5 years have passed since last update.

[Oculus] WebVR APIについて調べてみた

Last updated at Posted at 2015-02-02

[2015.06.03 追記]
WebVRのドラフトはGithub上にあるようです。


先日、FirefoxのNightlyビルドでWebVR APIが実装された、というニュースがありました。
Oculusを広めたい自分としては色々と興味がそそられるAPIです。

ちなみに、 Firefox Nightlyビルドで試したところ、Oculusを接続してからFirefoxを起動しないと認識されないようです。 なので、認識されない場合はFirefox自体を再起動することで認識されました。

ChromiumのWebVRを実装したビルドもあります。

今回作ったもの

せっかくなので、過去に配布されていたジプシー・デンジャーのモデルデータを使ってみました。

WebVR demo

動画はこちら↓
Oculus動画
https://www.youtube.com/watch?v=sZvQ_RslvuE&feature=youtu.be

上記デモはGithubに公開しています。(実際の動作デモはこちら)

※ ちなみに海の表現はThree.jsのexampleから拝借しました。VRRendererをそのまま利用すると海の反射がおかしなことになったのでちょっとだけ手を入れています。

Firefox NightlyへのWebVRはAdd-onとして提供されている

最初、VR Deviceを取得しようとしてそのAPI(navigator.getVRDevice)がなくて焦りました。
が、少し調べてみると、どうやらAdd-onという形で提供されているようです。
WEB VR LANDS IN FIREFOX NIGHTLYを参考にしました。Add-onはこちらです。(直接DLされます))

さらにAdd-onを追加しただけではダメで、Add-on追加後、Fileメニューから「New Non-e10s Window」を選択して専用のウィンドウを立ち上げる必要があります。

Getting Started

New Non-e10s Windowを立ち上げたらいよいよWebVRが利用できるようになります。
こちらの「Getting started with the WebVR API」に、ごく簡単なデモとそのコードが紹介されていました。

デモではThree.jsを組み合わせて実装されています。
Three.jsについての説明は今回は割愛します。デモで使われているWebVR APIとその表示方法に絞って書こうと思います。

VR Deviceの取得

OculusなどのVR Deviceを取得するには以下のように取得するメソッドを実行します。

getVR
if (navigator.getVRDevices) {
    navigator.getVRDevices().then(vrDeviceCallback);
}
else {
    console.log('WebVR is not supported!');
}

参考にした記事はmozGetVRDevicesもチェックして実行していましたが、現在のNightlyビルドではmozのプレフィクスはなくなっていました。

見てもらうと分かりますが、さりげなくPromiseが使われています。
デバイスの取得が非同期で行われるためでしょう。
そしてそのコールバックでデバイス取得の処理を行います。

vrDeviceCallback
function vrDeviceCallback(vrdevs) {
    for (var i = 0; i < vrdevs.length; ++i) {
        if (vrdevs[i] instanceof HMDVRDevice) {
            vrHMD = vrdevs[i];
            break;
        }
    }
    for (var i = 0; i < vrdevs.length; ++i) {
        if (vrdevs[i] instanceof PositionSensorVRDevice &&
            vrdevs[i].hardwareUnitId == vrHMD.hardwareUnitId) {
            vrHMDSensor = vrdevs[i];
            break;
        }
    }
    initScene();
    initRenderer();
    render();
}

最初のforループがHMDの本体を取得するループです。
次のforループは、HMDのセンサーを取得しています。
本体と同じhardwreUnitIdを持つものを取得しているのが分かると思います。

シーンのセットアップ

デバイスが取得できたら、そのままシーンのセットアップを行います。
といっても、ここは普通にThree.jsでのシーンのセットアップとまったく同じです。

setup-scene
function initScene() {
    camera = new THREE.PerspectiveCamera(60, 1280 / 800, 0.001, 10);
    camera.position.z = 2;
    scene = new THREE.Scene();
    var geometry = new THREE.IcosahedronGeometry(1, 1);
    var material = new THREE.MeshNormalMaterial();
    mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
}

レンダラーのセットアップ

続いてレンダラーのセットアップです。
こちらも基本的にはThree.jsのレンダラーのセットアップそのままです。
が、一点だけ異なるのはVR用のレンダラーを用意することです。

renderer-setup
function initRenderer() {
    renderCanvas = document.getElementById("render-canvas");
    renderer = new THREE.WebGLRenderer({
        canvas: renderCanvas,
    });
    renderer.setClearColor(0x555555);
    renderer.setSize(1280, 800, false);
    vrrenderer = new THREE.VRRenderer(renderer, vrHMD);
}

レンダリング

セットアップが終わったらレンダリングを行います。
レンダリングはvrrendererが担当します。
レンダリング時にカメラの位置と回転を、VR Deviceからの値でupdateすれば、OculusのVR体験が実現できる、というわけです。

render
function render() {
    requestAnimationFrame(render);
    mesh.rotation.y += 0.01;
    var state = vrHMDSensor.getState();
    camera.quaternion.set(state.orientation.x, 
                          state.orientation.y, 
                          state.orientation.z, 
                          state.orientation.w);
    vrrenderer.render(scene, camera);
}

歪み補正

ちなみに、Oculusはレンズを使って歪ませて視野を確保していますが、そのために、画面に出力される内容を、Oculusのレンズに合わせて歪み補正をする必要があります。

その補正を担当してくれているのがTHREE.VRRendererというわけです。

補正の詳しい計算については、上記のVRRendererの実装を見てもらうと分かります。
というか、ぶっちゃけなにをしているかよく分かりませんw
(ただ、通常のprojection matrixに似た計算を行っているので、Oculusの歪みを補正するSkew的な処理を入れてるのかも?)
HMDから得られるFOVから、色々計算していい感じに補正しているようです。

VRRenderer

デモで使われているThree.jsのプラグインという形で提供されているTHREE.VRRendererですが、コード自体はそれほど多くありません。

大きくやっていることを箇条書すると、

初期化時

  • HMDの目線位置を取得
  • HMDの左右のカメラのFieldOfViewを取得

update時

  • 渡されたカメラを2つに複製
  • 複製したカメラをそれぞれ左半分、右半分のレンダリングに使う
  • 複製したカメラのprojectionMatrixを、歪み補正をしたものに差し替え
  • カメラの位置を目線位置から算出して再計算

という流れでレンダリングを行っているようです。
これを実行することで、Oculusのコンテンツでよく見る、ふたつに画面分割されたものが表示されます。

HMDのトラッキング

最後に、update時にはHMDの状態をトラッキングし、それをカメラに反映させます。
具体的には以下のコードです。

head-traking
var state = vrHMDSensor.getState();
camera.quaternion.set(state.orientation.x, 
                      state.orientation.y, 
                      state.orientation.z, 
                      state.orientation.w);

最初に取得したVR Sensorから現在の状態を取得し、カメラに適用します。

フルスクリーンモード

上記までで、OculusからのデータをThree.jsの世界に反映することができました。
最後に実装するのはフルスクリーンモードです。特に、モニタとリンクさせている場合はフルスクリーンにしないと正常に見えません。

full-screen
window.addEventListener("keypress", function(e) {
    if (e.charCode == 'f'.charCodeAt(0)) {
        if (renderCanvas.mozRequestFullScreen) {
            renderCanvas.mozRequestFullScreen({
                vrDisplay: vrHMD
            });
        } else if (renderCanvas.webkitRequestFullscreen) {
            renderCanvas.webkitRequestFullscreen({
                vrDisplay: vrHMD,
            });
        }
    }
}, false);

デモではFキーを押すことでフルスクリーンになるように実装されています。
ここで行っていることは、フルスクリーン用のAPIに対して、{ vrDisplay: vrHMD }として、vrDisplayに、デバイス取得で取得したHMDのインスタンスを渡すことです。

これで完成です。

18
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
18