はじめに
こんにちは。GMOグローバルサイン・ホールディングス所属のはが(@mxcn3)です。
2024年に入って初めてのこのテックブログの投稿となりますが、過去にどんな記事を書いていたのかを見ていたところ、約2年前、このテックブログに「社内イベント用にウェブ展示会場を作ってみた」という記事を寄稿しました。
その中でボディトラッキング用の機材の購入に言及していました。現在はいくつかのVRプラットフォームで楽しんでいます。私自身、VRで遊んでいるときに周りのVRユーザーの話を聞くと、多くが「Vive Tracker」「Haritora」「Uni-motion」などの機器を使ってフルボディトラッキングを実現しているようです。
この記事について
約1週間ほど前、「Meta Quest Browser 32.0」のリリースノートが公開されました。このリリースノートを見ると、「WebXR: ボディトラッキングの実験的サポート」という新機能が追加されていることがわかります。
今回の記事は、ボディトラッキングに関連する内容で、「Meta Quest単体」と「Meta Quest Browser」を使ってボディトラッキングのAPIを利用した記事になります。
Meta Questでボディトラッキングを使う方法
- 実行環境の準備
- デモページにアクセス
- XRセッションの許可と開始
- 開始されたXRセッション
1. 実行環境の準備
Meta Questを使用している場合、特別なアプリケーションのインストールは必要ありません。標準でインストールされている「Meta Quest Browser」を利用します。このブラウザは、「WebXR Device API」に対応しており、ARおよびVRの体験を作成することが可能です。
2. デモページにアクセス
こちらのデモページのURLをブラウザから開きます。
3. XRセッションの許可と開始
Meta Quest BrowserでWebページにアクセスをして「Enter XR」をクリックをすると、「没入タイプのエクスペリエンスを開始」をするための権限の選択がでてきます。
許可をすることで、XRのセッションを開始をすることができます。
4. 開始されたXRセッション
実際のMeta Quest 3の動作はこちらとなります。
軽くジャンプをしたり、椅子に座ったりする動作を行ってみましたが、ある程度の精度で自分の動きをトラッキングできているように見えます。
実装について
WebXRの体験を作るために、ウェブサイトには、「JavaScript」・「WebGL」・「WebXR Device API」を組み合わせて実装をする必要があります。これらを組み合わせて開発するのに適しているJavaScriptライブラリとしては、「Three.js」「Babylon.js」「A-Frame」「PlayCanvas」などの選択肢がありますが、デモコードでは、PlayCanvasを採用しました。
PlayCanvasを使ったWebXRの実装
このデモのコードは、「Glitch」で公開しています。ページはHTML、JavaScript、CSSを使用して作成された静的なWebページです。
PlayCanvasでWebXRを実装する方法については、「PlayCanvasでのWebXRの使用方法」というマニュアルがありますので、これを参考に実装を進めていきます。
- HTML / JavaScript の作成
- WebXR対応
- ボディトラッキングに対応
1. HTML / JavaScript の作成
以下は、デモの一部を実装するための手順です。
まず、Canvasを使用してWebページを作成します。(CSSの設定等一部省略しています。詳細はコードを参照してください。)
<html>
<head>
<title>WebXRによるボディトラッキングの実装</title>
<!-- PlayCanvasライブラリの読み込み -->
<script src="https://code.playcanvas.com/playcanvas-stable.min.js"></script>
<script type="module" src="script.js"></script>
</head>
<body>
<!-- Canvas要素 -->
<canvas id="application-canvas"></canvas>
<!-- XRセッションを開始するボタン -->
<button id="startXr">Enter XR</button>
</body>
</html>
次に、WebXRを実行するためのPlayCanvasのJavaScriptの実装です。
// Canvas要素を取得
const canvas = document.getElementById("application-canvas");
// PlayCanvasアプリケーションの初期化
const app = new pc.Application(canvas, {
mouse: new pc.Mouse(canvas),
touch: new pc.TouchDevice(canvas),
keyboard: new pc.Keyboard(window),
});
// ライトの設定
const light = new pc.Entity("light");
light.addComponent("light");
app.root.addChild(light);
light.setEulerAngles(45, 0, 0);
// カメラの設定
const camera = new pc.Entity("camera");
camera.addComponent("camera", {});
camera.setPosition(0, 0, 3);
app.root.addChild(camera);
// アプリケーションの起動
app.start();
// XRセッションの開始
const startXr = () => {
if (app.xr.isAvailable(pc.XRTYPE_AR)) {
camera.camera.enabled = true;
camera.camera.clearColor = new pc.Color(0, 0, 0, 0);
app.xr.start(camera.camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
anchors: true,
meshDetection: true,
planeDetection: true,
optionalFeatures: ["body-tracking"],
});
} else if (app.xr.isAvailable(pc.XRTYPE_VR)) {
app.xr.start(camera.camera, pc.XRTYPE_VR, pc.XRSPACE_LOCALFLOOR, {
optionalFeatures: ["body-tracking"],
});
}
};
// XRセッション開始ボタンのイベントリスナー設定
document.getElementById("startXr").addEventListener("click", startXr);
PlayCanvasの初期化手順については、PlayCanvasリポジトリを参考にして実装をしています。
2. WebXR対応
// XRセッションを開始する
app.xr.start(camera.camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
anchors: true,
meshDetection: true,
planeDetection: true,
optionalFeatures: ["body-tracking"],
});
XRセッションを開始するには、app.xr.start
を実行します。第一引数には「カメラ」を、第二引数にはセッションのタイプ(ARまたはVR)を、第三引数には体験の種類(視点)の設定を行います。
第四引数では、使用するAPIを設定することで、WebXR Device APIの各機能が有効になります。今回の例では、anchors
、meshDetection
、planeDetection
の機能を要求しています。
また、今回使用するボディトラッキングについて、PlayCanvasはまだ対応していませんが、新しいAPIを使用する方法も提供されており、optionalFeatures
に配列として使用する機能を含めることで、PlayCanvasでは未実装のAPIを使用することができます。
現在、PlayCanvasが対応している機能については「Capabilities」を参照してください。
AR / VRそれぞれ対応について
Meta Quest 3や他のWebXR対応デバイスを使用している場合でアクセスをしたときに、ARとVRのどちらにも対応していないデバイスがあります。このようなデバイスでアクセスされたことを考慮する場合には、このように設定することでARを優先して実行し、VRのみに対応している場合はVRで実行するように設定できます。
const startXr = () => {
// ARが利用可能かどうかを確認
if (app.xr.isAvailable(pc.XRTYPE_AR)) {
// カメラを有効にし、カメラのクリアカラーを透明に設定します。
camera.camera.enabled = true;
camera.camera.clearColor = new pc.Color(0, 0, 0, 0);
// WebXRのARセッションを開始します。
app.xr.start(camera.camera, pc.XRTYPE_AR, pc.XRSPACE_LOCALFLOOR, {
anchors: true, // アンカーの使用を有効にします。
meshDetection: true, // メッシュ検出を有効にします。
planeDetection: true, // 平面検出を有効にします。
optionalFeatures: ["body-tracking"], // 追加機能としてボディトラッキングを指定します。
});
} else if (app.xr.isAvailable(pc.XRTYPE_VR)) { // VRが利用可能かどうかを確認
// WebXRのVRセッションを開始します。
app.xr.start(camera.camera, pc.XRTYPE_VR, pc.XRSPACE_LOCALFLOOR, {
optionalFeatures: ["body-tracking"], // 追加機能としてボディトラッキングを指定します。
});
}
};
この設定を行うことで、PlayCanvasを使用してWebXRセッションを開始するプログラムを作成できました。この状態でWebページにアクセスし、「ボタン」を押すと、WebXRセッションを開始することができます。許可後、パススルー表示を体験できます。
こちらを許可すると、パススルー表示が可能なWebページを体験できます。
3. ボディトラッキングに対応
PlayCanvasの初期化コードの下に、ボディトラッキング機能を実装するためのスクリプトを追加します。全身のジョイント位置と角度を取得できるBody TrackingのAPIを使用し、PlayCanvas上でこれらをシンプルな形状(Cone)にマッピングすることで、体の位置をリアルタイムで可視化します。
// Body Tracking機能を実装するためのスクリプトを定義します。
const ModuleBodyTracking = pc.createScript("moduleBodyTracking");
// 初期化関数です。ボディトラッキングに必要な設定を行います。
ModuleBodyTracking.prototype.initialize = function () {
this.jointEntities = {}; // 各ジョイントを表すエンティティのマップです。
// 自分自身を表すエンティティを作成し、初期位置と向きを設定します。
this.selfEntity = new pc.Entity("selfEntity");
this.selfEntity.setLocalPosition(0, 0, -1);
this.selfEntity.setLocalEulerAngles(0, 180, 0);
app.root.addChild(this.selfEntity); // シーンのルートに追加します。
// WebXRのフレーム更新時に呼び出される関数を登録します。
app.xr.on("update", this.xrUpdate, this);
};
// WebXRのフレーム更新時に実行される関数です。
ModuleBodyTracking.prototype.xrUpdate = function (frame) {
if (frame.body) { // ボディトラッキングのデータがある場合
const body = frame.body;
body.forEach((xrBodySpace, idx) => {
const jointName = xrBodySpace.jointName; // ジョイントの名前
if (!this.jointEntities[jointName]) { // ジョイントのエンティティが未作成の場合
const jointEntity = new pc.Entity(jointName); // ジョイントエンティティを作成
jointEntity.addComponent("render", { type: "cone" }); // レンダリングコンポーネントを追加
// ジョイントのサイズを設定(手のジョイントは小さく、それ以外はやや大きくします)
const scale = new pc.Vec3(jointName.includes("hand") ? 0.01 : 0.02, jointName.includes("hand") ? 0.01 : 0.02, jointName.includes("hand") ? 0.01 : 0.02);
jointEntity.setLocalScale(scale);
this.selfEntity.addChild(jointEntity); // 自身のエンティティにジョイントエンティティを追加
this.jointEntities[jointName] = jointEntity; // マップにジョイントエンティティを登録
}
// ジョイントのポーズ(位置と回転)を取得し、エンティティに反映します。
const pose = frame.getPose(xrBodySpace, app.xr._referenceSpace);
if (pose) { // ポーズが取得できた場合
const position = new pc.Vec3(pose.transform.position.x, pose.transform.position.y, pose.transform.position.z);
const rotation = new pc.Quat(pose.transform.orientation.x, pose.transform.orientation.y, pose.transform.orientation.z, pose.transform.orientation.w);
this.jointEntities[jointName].setLocalPosition(position);
this.jointEntities[jointName].setLocalRotation(rotation);
}
});
}
};
// スクリプトコンポーネントをルートに追加し、ボディトラッキングモジュールを作成します。
app.root.addComponent("script");
app.root.script.create("moduleBodyTracking");
取得できる体の部位について
全身の各部位の位置と角度を取得できます。全体で84箇所のデータを取得できました。これらのデータをPlayCanvasのプログラムでシンプルな球体(Sphere)にマッピングし、可視化することができます。
この状態で表示をするとこのようなデモのような形で表示をすることができます。PlayCanvas上のプリミティブな形状の位置に反映させることで、体の動きに合わせたアバターを表示できます。
WebXRセッションでのデバッグについて
Meta QuestなどのブラウザでWebXRのセッション中にJavaScriptのコンソールを確認する際のコンソールの情報を確認するために、「vConsole」といったライブラリを利用してコンソールに表示しています。
まとめ
PlayCanvasとWebXR Body Tracking APIを組み合わせることで、ボディトラッキングを利用したWebXRアプリケーションの開発を比較的簡単に行うことができました。
本記事では基本的な実装方法を紹介しましたが、さらに洗練されたアバター表示やボディトラッキングを用いたインタラクションなど、多様な応用が考えられます。
質問や不明点がありましたら、Twitterの@mxcn3までお問い合わせください。