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