本記事はクラスター Advent Calendar 2024 9日目の記事です。
記事でやること
WebXRでキャラクター描画・操作を試してみる(動作確認はVision Proの例)
はじめに
こんにちは、こうたといいます。フロントエンドエンジニアをやっています。ただ直近はUnityでの業務が多めでやっています。
みなさん、Vision ProやQuest3は持っていますか?
clusterを遊んだり、開発している人にとっては頭につける機器を複数買ってしまうことはよくあることだと思います。
VR/ARデバイスを複数所有していると、それらを活用して何か開発したくなりますよね。しかし、Vision Pro用、Quest3用と個別にアプリを作るのは大変です。
そこで、WebXRを活用して、どちらのデバイスでも動くアプリを作ってみるのはどうでしょうか?
WebXRは、ウェブ上でARやVRを実現する技術で、Quest 3やVision Proで動作させることが可能です。
この記事では、Unityで開発しWebXRとして出力する方法を取ろうと思います。
clusterはUnityで開発されているので、この方法をとることでUnityの知識の恩恵を得ることができそうです。
今回は、WebXR Exporterを利用してWebXRアプリを開発し、キャラクター描画とインタラクションを試してみます。
記事で解説しないこと
- WebXRについての詳細の説明
- Unityに関する操作・実現方法の詳細
unity-webxr-exportについて
WebXR Exporterは、UnityからWebXR対応アプリを出力できるツールです。導入方法は公式のGetting Startedを参照してください。すぐ試したい場合は、MainProjectを利用するのが便利です。
このツールは更新も活発で、Unity 6にも対応しています。
開発をしてみよう
Unityを使ってWebXRの出力ができるようになったので、サンプルのようなものを試してみようと思います
作るもの
WebXRでアバターを描画し、インタラクションできる仕組みを作ります。
使ったアセット
-
FinalIK
- キャラクターへのIKの設定に使用
-
PuppetMaster
- キャラクターへのCollider設定とインタラクションのために使用
やったこと
-
キャラクターの描画とIK設定
IKの設定はFinalIKを使用し、キャラクターにFullBodyBipedIKを適用しました。PuppetMasterを使いColliderを設定します -
XR Interaction WebXR Setupの利用
手の動きを反映するため、XR Interaction WebXR Setup
をシーンに配置します。このPrefabはwebxr-interactionsのサンプルに含まれています -
インタラクション設定
手の動きに合わせてキャラクターを操作可能にするため、手のGameObject(Left Hand
とRight Hand
)の子オブジェクトにColliderを追加しました。これにより、手で触れる動きがキャラクターに反映されます
動作結果
キャラクターを描画してインタラクションすることができました(動作確認はわかりやすいようにColliderはSphereでMeshを描画してます)。
デバッグ方法
WebXRDeviceAPIはセキュアなコンテキストのみでしか実行することができません(参考)。
Vision Proで確認したい際には、Netlifyなどにデプロイなどが必要になります。
もしくは、自己署名証明書などを作成して、ローカル環境をhttps対応させるなどでも動作確認できます(あくまでのローカルのデバッグ用として)。
最後に
UnityでWebXR出力したプロジェクトをVisionProとQuest3で動かしてみて、普段のUnity開発と同じ開発体験でWebXRを動かせることがわかりました。
パフォーマンス面やシェーダーに関する部分は見きれてないのですが、ある程度はUnityでWebXRの開発ができそうです。
ちなみにclusterでゲーム作成やワールド作成をすると、なんとVR対応のほかにMobileでも対応してしまいます!
さらにマルチプレイでの同期という、かなり大変な問題も解決できちゃいます。
みなさんはぜひともclusterでワールド作成をしてみることをお勧めします!
おまけ
unity-webxr-exportを導入することでWebXR出力ができるようになりました。
このパッケージは何をやっているのかの疑問がわいてきたので、中身を少し見ることができた範囲で書いてみようと思います。
WebXRの起動について
まず、WebXRの起動がどのように行われているのかを見てみましょう。
最初の実行に必要なメニューはWebXRMenuに実装されていたので中身を見てみます。
こちらはHiddenのWebGLTemplateをAssetsにコピーする形となっていました。
このTemplateはPlayterのWebGLTemplateに指定しています。では、このTemplateでは何をしているのでしょうか?
index.htmlの中身には、VR/ARを有効にするためのButtonが実装されており、こちらはUnityのToggleを呼び出すようになっていました。toggleVRの中身をみることで何をしているのかがわかりそうです。
では、toggleVRの中身を見てみましょう。
UnityではPluginsにjslibやjspreを配置することで相互作用を実現しており、ライブラリではwebxr.jspreで実現しているようです。
VRの開始は以下のコードで実現しています (参考1、参考2)。
XRManager.prototype.onRequestVRSession = function () {
if (!this.isVRSupported) return;
if (this.BrowserObject.pauseAsyncCallbacks) {
this.BrowserObject.pauseAsyncCallbacks();
}
this.BrowserObject.mainLoop.pause();
var thisXRMananger = this;
var tempRender = function () {
thisXRMananger.ctx.clearColor(0, 0, 0, 0);
thisXRMananger.ctx.clear(thisXRMananger.ctx.COLOR_BUFFER_BIT | thisXRMananger.ctx.DEPTH_BUFFER_BIT);
}
window.requestAnimationFrame( tempRender );
navigator.xr.requestSession('immersive-vr', {
requiredFeatures: thisXRMananger.gameModule.WebXR.Settings.VRRequiredReferenceSpace,
optionalFeatures: thisXRMananger.gameModule.WebXR.Settings.VROptionalFeatures
}).then(function (session) {
session.isImmersive = true;
session.isInSession = true;
中身としては、WebXRのセッションの起動を要求している形になっているようです(参考のMDN)。
トラッキングデータの取り扱い
ここまでで、webxrのセッションを開始する方法がわかりました。
では、コントローラーや手の動きはどのように渡されているのでしょうか?
webxr.jspreのControllerを眺めてみましょう。
XRManager.prototype.getXRControllersData = function(frame, inputSources, refSpace, xrData) {
Module.HEAPF32[xrData.handLeft.frameIndex] = xrData.frameNumber; // XRHandData.frame
Module.HEAPF32[xrData.handRight.frameIndex] = xrData.frameNumber; // XRHandData.frame
Module.HEAPF32[xrData.handLeft.enabledIndex] = 0; // XRHandData.enabled
Module.HEAPF32[xrData.handRight.enabledIndex] = 0; // XRHandData.enabled
Module.HEAPF32[xrData.controllerA.frameIndex] = xrData.frameNumber; // XRControllerData.frame
Module.HEAPF32[xrData.controllerB.frameIndex] = xrData.frameNumber; // XRControllerData.frame
Module.HEAPF32[xrData.controllerA.enabledIndex] = 0; // XRControllerData.enabled
Module.HEAPF32[xrData.controllerB.enabledIndex] = 0; // XRControllerData.enabled
...
getXRControllersData でトラッキングデータをUnityのヒープメモリに書き込んでいるのがわかります
書き込まれたデータを扱っている個所を探してみると、HandTrackingに関しては、WebXRSubsystemにて扱っている様子が見えました。
このクラスでは、Nativeと共有配列を用いてデータのやり取りを行っており、手のトラッキングに関しては、GetHandFromHandsArray
を介して、データの更新を行っているようです。
bool GetHandFromHandsArray(int handIndex, ref WebXRHandData handObject)
{
int arrayPosition = handIndex * 212;
int frameNumber = (int)handsArray[arrayPosition++];
if (handObject.frame == frameNumber)
{
return false;
}
...
こうして受けとったデータはXRPluginにてデータをUnityEngine.XRのProviderなどで扱えるようにしているのが確認でき、あとは通常のUnity開発と同じ流れで扱えそうです。
このようにして、WebXRで受け取ったデータを扱える形に整えて実現しているようです。カメラなども同じく受信して整えている個所があったので見てみると分かるかもしれないですね。
今回はここまでで一旦中身を見るのは終わろうと思います。