LoginSignup
8
6

More than 5 years have passed since last update.

[WebVR] Gamepad APIを使ってHTC Vive Controllerを表示する

Posted at

この記事はWebGL Advent Calendar 2016 18日目の記事です。

とか言いながら、ほぼWebVR、しかもWebGLあんまり関係ないのはご了承ください;
(いちおうWebVRにはWebGL必要だよねってことで( ;´Д`))

概要

Oculus Touchがいよいよ発売され、VR空間で「手」を認識してあれこれやることがスタンダードになってきました。
HTC Viveは当初からコントローラがついていて、HMD同様にトラッキングできていました。

今回の記事は、そのHTC ViveのコントローラをWebVRでも利用するためのセットアップについて書きたいと思います。

ちなみに今回のサンプルはGithubにアップしてあります。(ベースは「WebVR Boilerplate」です)
そして今回のサンプルは、Three.jsのVive paintデモであったものを参考にさせてもらいました。

Gamepad API

WebVRに関連するものなので、そのAPIにあるのかなーと思いきや、仕様自体はGamepad APIがそれを担っているようです。
Gamepadの取得は以下のようにします。

// 指定したIDのGamepadを探す
function findGamepad(id)
{
    var gamepads = navigator.getGamepads();
    for (var i = 0, j = 0; i < 4; i++)
    {
        // 取得したゲームパッドの中から「OpenVR Gamepad」を探す
        var gamepad = gamepads[i];
        if (gamepad && gamepad.id === 'OpenVR Gamepad')
        {
            if (j === id)
            {
                return gamepad;
            }
            j++;
        }
    }
}

findGamepad関数では、navigator.getGamepad();を利用して接続されているゲームパッドのリストを取得します。
そして取得したリストの中からOpenVR GamepadのIDが付いているものを探し、さらに関数の引数で指定したインデックスにあるGamepadを返しています。

最初、Gamepadがどうしても取れないなーと思っていたんですが、コントローラのトリガーを引くなどして認識させないとならない点に注意が必要です。
(じゃないといつまでたってもGamepadはnullのまま・・)

Updateで姿勢制御

さて、上記のようにすることでゲームパッド(Viveコントローラ)が取得できました。
が、VRのコントローラは位置を常にUpdateしないとなりません。
ということで、位置の同期を取るコードは以下のようになります。

this.update = function ()
{
    // 指定したIDのコントローラを探してくる
    // (認識されていなかったり、トラッキングから外れると取得できなくなるため)
    gamepad = findGamepad(id);

    // ゲームパッドが無効な場合は非表示にして更新はしない
    if (gamepad === undefined || gamepad.pose === undefined)
    {
        scope.visible = false;
        return;
    }

    if (gamepad.pose === null) { return; }


    // コントローラの制御
    var pose = gamepad.pose;
    if (pose.position !== null) { scope.position.fromArray(pose.position); }
    if (pose.orientation !== null) { scope.quaternion.fromArray(pose.orientation); }

    // デバイスからの情報を元に姿勢を更新し、ルームスケールのマトリクスを掛けて最終的な姿勢にする
    scope.matrix.compose(scope.position, scope.quaternion, scope.scale);
    scope.matrix.multiplyMatrices(scope.standingMatrix, scope.matrix);
    scope.matrixWorldNeedsUpdate = true;
    scope.visible = true;
}

やっていること自体はとてもシンプルです。
ゲームパッドから姿勢を取り出し、それをモデルに伝えているだけ。
ただし、VRのコントローラはトラッキングから外れるとnullになって取得できなくなるので、その確認処理が入ることに注意が必要です。

ゲームパッドが無事に取得できた場合は、poseプロパティに必要なデータ(位置と回転)が入っているので、それをモデルに適用してやります。
そしてこれまた注意点ですが、この姿勢制御だけでは位置がおかしなことになります。
なぜなら、VRは空間を認識しているからです。
つまり、コントローラの位置や回転の値は、その空間内を基準としたものになるため、その空間を親としたマトリクスをかけてやる必要があるわけです。

そしてそれを行っているのが以下の一行です。

scope.matrix.multiplyMatrices(scope.standingMatrix, scope.matrix);

このstandingMatrixは、Three.jsのVRControlsにあるマトリクスです。
(コントローラ生成後に、生成側のスクリプトでそれを設定しています)

controller1.standingMatrix = controls.getStandingMatrix();

こうすることによって、無事、手の位置にあるコントローラと同じように動く姿勢が得られます。
今回のサンプルではViveコントローラのモデルを表示していますが、当然、コントローラ以外のモデル(例えば手とか)を利用してもOKです。
その場合は、表示したいモデルを読み込んだあとに、そのモデルをViveController(今回作成したサンプル用のクラス)の子としてaddしてやれば大丈夫です。

ボタンの種類

姿勢に関しては上記の方法で問題なく同期されます。
ただ、コントローラである以上はその状態を取得して、適切に操作できなければなりません。
ゲームパッドにはbuttonsというプロパティがあるので、それを参照して、どのボタンがどういう状態なのか、を知ることができます。

ViveのControllerで定義されているボタンは以下の通り。
※ 参考にさせてもらったThree.jsではbuttons[2]がメニューボタンとしてあったんですが、(途中で変更されたのか)buttons[3]のものが正しかったです。

  • button[0] = タッチパッドボタン
  • button[1] = トリガー
  • button[2] = グリップ左右のボタン
  • button[3] = メニューボタン

また、Vive Controllerにはタッチパッドが付いているため、それを、軸の情報として取得することができます。
その参照は以下になります。

  • axes = タッチパッドの触れた位置

これらの情報から、適切に情報を取り出し、画面にフィードバックしてやることで、VR空間内での操作が可能となります。
今まではOculusで映像を見るのが精一杯でしたが、コントローラを使えるようになると、VRでやれることが格段に増えるのでとても楽しいですね。
ぜひ、みなさんも楽しいWebVRライフを送ってください。

それと、ブラウザの(有効化などの)設定については以下のサイトを参考にしました。
http://meta4vr.net/setup/

※ 現状ではまだ、正式にWebVRに対応したブラウザが出ておらず、どれも実験的なもののため、適切にフラグを有効化してやる必要があります。

8
6
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
8
6