Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

この記事は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に対応したブラウザが出ておらず、どれも実験的なもののため、適切にフラグを有効化してやる必要があります。

edo_m18
現在はUnity ARエンジニア。 主にARのコンテンツ制作をしています。 最近は機械学習にも興味が出て勉強中です。 Unityに関するブログは別で書いています↓ https://edom18.hateblo.jp/
http://edom18.hateblo.jp/
unity-game-dev-guild
趣味・仕事問わずUnityでゲームを作っている開発者のみで構成されるオンラインコミュニティです。Unityでゲームを開発・運用するにあたって必要なあらゆる知見を共有することを目的とします。
https://unity-game-dev-guild.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした