Meta Quest + Unity + WebXR Export + VRIK でアバター(unitychan)を操作する

やりたいこと

Meta Quest + Unity + WebXR Export + VRIK でアバター(unitychan)を操作したい!
すでに世の中にUnityとVRIKでアバターを操作する記事がいくつか存在しますが、WebXR Exportを使用した例はなかったため、知見を公開します。

環境

HMD: Meta(Oculus) Quest 2
Unityバージョン: 2021.3.12f1

UnityEditorをインストールする

Unity Hubを使用して、Unity 2021.3.xx のエディタをインストールします。
その際Android、WebGL、Windows向けのビルドができるようにしておきます。

その後、該当のバージョンのエディタで新しいプロジェクト(3D)を作成します。

WebXR Exportを導入する

WebXR ExportはUnityのパッケージです。このパッケージを利用すると、WebXR対応のVRアプリケーションを作成することができます。以下手順でWebXR Exportを導入していきます。

  1. EditメニューからProject Settingsを開く。
  2. Package Manager のScoped Registryで、以下の情報を設定。
    Name: OpenUPM
    URL: https://package.openupm.com
    Scope(s): com.de-panther

  3. Saveボタンを押す。
  4. ProjectSettings画面を閉じる。
  5. Windowメニューから Package Managerを開く。
  6. Packages:In ProjectをPackages:My Registriesに切り替え、WebXR ExportとWebXR Interactionsをinstallする。
  7. Window > WebXR > Copy WebGLTemplatesを選択する。
  8. Project Settings画面を開き、WebGL設定タブでWebXR Export plug-in providerにチェックをする。
  9. Window > Package Manager > WebXR Interactions > Sample Scene > Importを選択する。
  10. Project Settings > Player > Resolution and Presentation, で、WebGL TemplateにWebXR2020を選択する。
  11. Assets/Samples/WebXR Interactions/0.15.0-preview/Sample Scene/Scenes/Desert.unityを選択し、サンプルシーンを表示する。
  12. ProjectSettings > Player のWebGLタブでOtherSettings > Auto Graphic API のチェックを外す。
  13. Build Settings画面を開き、WebGLを選択しプラットフォームを切り替える。
  14. Build and Runでブラウザが開き、サンプルシーンが表示されることを確認する。
  15. 起動時のシーンに戻り、Assets/Samples/WebXR Interactions/0.15.0-preview/Sample Scene/Prefabs/WebXRCameraSet.prefab をHierarchyウインドウにドラッグ&ドロップする。
  16. Player Settings > Publishing SettingsのDecompression Fallbackをオンにしておく。

Final IK(VRIK)を導入する

Final IK(VRIK)は、HMD・コントローラの位置からモデルの姿勢を計算し、操作してくれるアセットです。

  • Asset StoreでFinal IKを購入します。
    (よく半額セールされるのでそのタイミングで購入するのがオススメです。)
  • PackageManagerでFinal IK をimportする。

unitychanをインポートする

操作するアバター(モデル)として、unitychanを導入していきます。以下に手順を示します。

  • 以下サイトでunitychanをダウンロードする。
    https://unity-chan.com/contents/guideline/?id=UnityChan
  • UnityChan_1_3.unitypackageをカスタムインポート(Assets->import package ->Custom Pcakcage)する
  • シーンにAssets/UnityChan/Models/unitychan.fbx を配置する。
  • unitychanをWebXRCameraSet直下に配置する。
    その際、unitychanのPositionは0,0,0にする。

VRIK設定

VRIKを設定することでアバターを操作することができるようになります。以下に手順を示します。

  • unitychanにadd componentでVRIKを追加する。
    (Referencesが自動で設定されることを確認する。)
  • 空オブジェクトを生成し、LeftHandTargetと命名し、WebXRCameraSet/handL の直下に配置する。
    Transformを以下の通りとする。
    Position(0,0,0)
    Rotation(0,90,0) ※1
    Scale(1,1,1)
  • 空オブジェクトを生成し、RightHandTargetと命名し、WebXRCameraSet/handR の直下に配置する。
    Transformを以下の通りとする。
    Position(0,0,0)
    Rotation(0,90,0) ※1
    Scale(1,1,1)
  • 空オブジェクトを生成し、HeadTargetと命名し、WebXRCameraSet/Cameras/CameraFollower の直下に配置する。
    Transformを以下の通りとする。
    Position(0,0,-0.05) ※2
    Rotation(0,-90,-90) ※1
    Scale(1,1,1)

※1 VRIKのモデルの向きとunitycanのモデルの向きが違うので、Rotationを変更して調整する
※2 カメラとモデルの頭の位置が完全にかぶると、モデルの頭のせいで前が見えなくなるので、頭の位置を若干後ろにする

  • unitychanのVRIKのSolver値を設定をする
  • Plant Feetのチェックボックスを外す
  • Spine/Head Target に作成したHeadTargetオブジェクトを割り当てる
  • Spine->Left Arm->Hand->Target に作成したLeftHandTargetオブジェクトを割り当てる
  • Spine->Right Arm->Hand->Target に作成したRightHandTargetオブジェクトを割り当てる

自分の姿を映すモニタ(姿見)を作成する

これまでの記事で、アバターを操作できるようになっているはずですが、せっかくのアバターを自分自身(主観視点)では見ることができません。自分の姿を見られるモニタを作成していきましょう。

  • Assetsに空のRenderTextureを追加し、CameraTextureにリネームする
  • CameraTextureのSizeを1024×1024にする
  • シーンに新規カメラを追加し、以下通り設定する
    Position :(0,0.5,2)
    Rotation :(0,180,0)
  • 追加したカメラのTargetTextureにCameraTextureを設定する
  • シーンに 3DObject > Quad を追加
    以下通り設定し、unitychanの前に配置する
    Position :(0,1,2)
    Rotation :(0,0,0)
    Scale :(2,2,2)
  • QuadにCameraTextureをドラッグ&ドロップ
    これで自分の姿を映すモニタが完成です!

位置や角度を変更するスクリプト

せっかくなので、コントローラで移動したり視点を回転したりする処理も入れていきましょう。以下に手順を示します。

  • Assetsの右クリックメニューから Create > Assembly Defnition を選択し、WebXRDefと名づける。
  • Assembly Definition Referencesで、「WebXR」を選択する。
  • WebXRDefと同階層に、以下スクリプトを用意する。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebXR;

public class WebXRCameraSetController : MonoBehaviour
{
    public Transform webXRCameraSetTransform;
    // 高さ調整コントローラ
    public WebXRController heightController;
    // 水平移動コントローラ
    public WebXRController moveController;
    // 回転コントローラ
    public WebXRController rotateController;

    public float speed = 1F;
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (this.webXRCameraSetTransform) {
            // 高さの移動
            var p = this.webXRCameraSetTransform.transform.localPosition;
            if (heightController.GetButton(WebXRController.ButtonTypes.ButtonA)) {
                 p.y -= 0.01f;
            } else if (heightController.GetButton(WebXRController.ButtonTypes.ButtonB)) {
                p.y += 0.01f;
            }

            // 水平移動
            var move = moveController.GetAxis2D(WebXRController.Axis2DTypes.Thumbstick);
            if (!move.Equals(Vector2.zero))
            {
                Vector3 forwardMove = this.webXRCameraSetTransform.transform.forward * (move.y * speed * Time.deltaTime);
                Vector3 rightMove = this.webXRCameraSetTransform.transform.right * (move.x * speed * Time.deltaTime);
                p += forwardMove + rightMove;
            }

            this.webXRCameraSetTransform.transform.localPosition = p;

            // その場で回転
            var rotate = rotateController.GetAxis2D(WebXRController.Axis2DTypes.Thumbstick);
            if (0 < rotate.x) {
                this.webXRCameraSetTransform.transform.RotateAround(p,Vector3.up,10);
            } else if (rotate.x < 0) {
                this.webXRCameraSetTransform.transform.RotateAround(p,Vector3.up,-10);
            }
        }
    }
}
  • スクリプトを任意のGameObjectに割り当てる。
  • webXRCameraSetTransformにWebXRCameraSetを割り当てる。
  • heightControllerにhandRを割り当てる。
    これにより、右コントローラのボタンで高さ調節が可能になる。
  • moveControllerにhandLを割り当てる。
    これにより、左コントローラのサムスティックで移動が可能になる。
  • rotateControllerにhandRを割り当てる。
    これにより、右コントローラのサムスティックで回転が可能になる。

ビルド・実行

  • Build Settings でWebGLを選択し、ビルドを実行します。
    出力されたファイルを動かすためには、Webサーバーを用意する必要があります。
    ファイルの静的ホスティングのみで動きますので、ApacheやAWS S3など利用してWebサーバーを用意し、ビルドしたファイルを展開してください。その際、https(SSL)通信でないと動作しないことにご注意ください。ただしlocalhostへの接続の場合は、http(非SSL)通信が可能です。
    VSCodeの拡張機能(LiveServer)などを使用すると、localhostに簡単にサーバーをたてることができます。
  • 用意したサーバにOculus Browserでアクセスすると、VRアプリを起動することができます。

この作品はユニティちゃんライセンス条項の元に提供されています

その他

  • handL(R) > modelのSkinnedMesh Rendererのチェックボックスを外すと、WebXRCameraSetのデフォルトの手を非表示にできます。
  • 視点がモデルの足元になる事象が発生した場合、ProjectSettings > XR Plug-in Management > WebXR > VR Required Reference Space の項目が Local_floorになっていない可能性があります。

まとめ

Meta Quest + Unity + WebXR Export + VRIK でアバター(unitychan)を操作することができました。

VRIKアセットがWebGLで動いてくれるか不安でしたが、素直に動いてくれました。

HeadTargetをCameraFollower の直下に置くことに気づかず若干ハマりましたが、終わってみれば設定・実装も少なく実現できました。

歩行モーションをもう少し自然にするなど、改善できそうなところは多いので、実現ができたらまた記事にしたいと思います。

参考

Unity + WebXR開発メモ(Meta Quest、Chrome、Firefox対応)
https://framesynthesis.jp/tech/unity/webxr/

Unity + FinalIK(VRIK) + Oculus Quest2 で unitychan を一通り動かす
https://qiita.com/szgk/items/f90fa74683bd010cefc0