見出し画像

バーチャルモーションキャプチャーへのプルリクまとめと、内部構造メモ

はじめに

今まで自分が投げたプルリクエストのまとめ(マージせずCloseしたものを除く)と、ばもきゃの中身をいじりたい人向けの解説を書きます。

※普通の使い方を知りたい人は、各種解説を読んで下さい。
 バーチャルモーションキャプチャーのFanboxのDiscordに参加するのも良いです。

↓の2022年改訂版みたいな。

プルリクエストのまとめ

#12 ボーン情報の外部送信機能の追加 (2019/10)

EVMC4U(ボーン受信スクリプト)と合わせて実装した、送信側機能です。
現在のVMCProtocolの始まりであり、現状のVMCProtocolサンプルスクリプトと同等の非常にシンプルな作り(ボーンと時刻を投げるだけ)から始まりました。
ただし、この時点では実際のところ現在のプロトコルと互換性がありません。

#14 ボーン情報の外部送信機能のroot位置送信、Blendsharp送信、プロトコル変更、負荷軽減 (2019/10)

ここでVMCProtocolとしてまともな動作をするようになります。
余談ですが、今でもこのバージョンのバーチャルモーションキャプチャーと互換性があるはずです。


#17 Add current Camera status sender. Add Camera Changed Action. (2019/10)

カメラが連動してほしいよね~という話から、EVMC4Uとカメラを連動させる機能が追加されました。
同時に、キー入力やコントローラ入力の転送機能も追加されています。
単なるモーション転送から、スタジオ作成のためのプロトコルとして進化しています。

#18 MIDI CC対応 (2019/10)

ばもきゃに搭載されていたMIDI Note機能を拡張し、MIDI CCノブに対応。キーボード操作と同等の情報として受け取れるようにした。
その上で、MIDI NoteとMIDI CCをEVMC4Uに送信する機能を追加しています。
ようは、MIDIのつまみやボタンを活用するための機能です。

#20 通信のフレーム分周とMidiCCWarpperの追加 (2019/10)

VMCProtocolの通信が受信側の負荷によって詰まるため、送信頻度を落とす機能を実装。
また、MIDI CC操作時にMIDI Jackからめちゃくちゃな頻度で呼ばれるため、いろいろなところの動きがおかしくなる挙動をするために、一旦配列で受けてフレーム単位で処理するようにするWarpperを実装。

この際、MIDI CCつまみ(アナログ値)だけでなく、MIDI CCボタン(デジタル値)として使う機能を追加。
これによってMIDI CCボタンをまともにボタンとして使えるようになりました。

#21 MIDI, OSC機能追加 (2019/10)

MIDIキーボードのVelocity(押下速度)をEVMC4Uに送れるようにしました。
また、Velocity=0のNote Onを送ってきたらNote Off相当にする処理を追加。(これはMIDIの規格上通信量を節約できるため、そういう風に送ってくるMIDIキーボードが普通にあるため。)

また、MIDI CCを外部送信する時の引数修正(なんでだっけ?)
余談だが、この頃はまだ2アプリ間プロトコルでしかなかったので割と適当でした。

また、EVMC4UにHMD、コントローラ、トラッカー位置情報を送る仕組みを追加して、Unity側で物理デバイスと連動させられる仕組みを追加。

下記動画を見て需要を感じたからだったと思います。
(この動画はカメラトラッカー連動機能を使って結構無理やりやってるのである。EVMC4Uを活用してくれていてとても楽しかった)

なお、このタイミングでEVMC4Uの構造を全面的に作り直しているらしい。(忘れてた)
デイジーチェーンの仕組みを入れたのがこの辺ですね。多分、ExternalReceiverが肥大化しすぎて困ったので、機能分割したんじゃなかったかと思う。

#22 全画面マニュアルの追加 (2019/10)
#82 全画面解説を現行バージョン向けに更新 (2021/8)

マニュアル整備。ばもきゃのすべての画面の解説をしています。サポート負荷軽減。

#23 OSC受信機能の追加とスペルミス・バグの修正 (2019/11)

ばもきゃ側で受信する機能を追加。
OSCでカメラ受信したり、仮想コントローラやらトラッカーやら表情やら視線やら、やたらめったら色々受信する機能が付きました。
これにより、様々な連携ができるようになりました。表情の外部入力や、SteamVRに依存しないトラッキング(QuestやWebカメラによるトラッキング)など。

当時は、ばもきゃ→ばもきゃ で通信することを想定していたっぽい。
今のwaidayo(face2vmc)のユースケースが生まれる前。

さらに、MIDI CCつまみで表情制御する機能も付きました。
が、UIが大変なことになっていた(このときはまだUnity側しかいじらないマンだったので、あきら氏に多大な負担をかけていた申し訳ない)

ちなみに、シネスイッチャーがリリースされたのはこのタイミングらしい。

#24 不適切な箇所の修正とコメント追加 (2019/11)

自原因バグの修正

#29 腰飛び防止機能、トラッカー情報送信の不足の修正 (2019/11)

トラッキング飛んだときにブラックホール(原点)に吸い込まれるのを対策す
る機能。
オーバーレイ開発で培ったOpenVR知識が生きています。(この後VirtualMotionTracker開発でも生きる)
Lighthouseのトラッキングシステムは、トラッキングがとんでもある程度補間しようと頑張ります。が、その頑張りが原因でスイーッと吹っ飛んでいったり、それもできなくなると原点(0,0,0)に飛ばされます。

実は、スイーッの状態では「Calibrating_OutOfRange」になっています。もっと言えば、正常にトラッキングできていないときは「Running_OK以外」になっています。

この状態を検出して、最後に取得できた座標にロック。一定時間正常に戻ったら少しなめらかに戻すという仕組みを入れています。
これにより、身体がぶっ飛んでいくことがなくなりました。

なお、この機能を実装した結果、ユーザーによって「足と腰のトラッカーの電源を切って椅子に身体を固定する」という使い方が考案されました。

さらにこのプリクエストでは、EVMC4U向けに無補正のトラッカー情報を送信する機能が追加されています(従来、アバタースケール補正がかかっていたため)

#34 bundleを用いたOSC送信に変更 (2020/1)

パケットが多すぎて詰まっている問題が発生していたため対応。

原因は、当時でバッグに使っていたツールがbundleに対応していないのもあり、単発でパケットを投げていたため。
そのため、1フレームあたりボーン数+表情の数+デバイス(30~60パケットほど)の数の小さい大量のUDPパケットが飛んでいき、送受信のIOキューを詰まらせていた。
詰まると何がおきるかというと、アバターの動きが遅れ続けるようになる。

これはPCスペックと、表情の数などで発生度合いが変わるため、自分の環境ではあまり問題が起きていなかったが、使う人が増えるほどに報告が増えていた。

対策として、OSCにはbundleという複数の値をまとめて1回で送信する機能があるため、これを使ってボーン等をまとめるようにした。
これにより、1フレームあたり6パケットほどにまで減ったため、トラブルが減っている(ただし、表情の数によっては重くなる)

なお、バーチャルモーションキャプチャー以外の送信元アプリで動きが遅くなる問題が出た場合は、ここを疑ってみてください。(パケットキャプチャするか、OSC解析ツールを使うと確認できます)

受信アプリは、bundle化されているかしないかにかかわらず動作する必要があります。(uOSCを使っていれば問題なく処理されます)

#36 OSC送信機能の拡張 (2020/1)

バーチャルモーションキャプチャーでの外見を外部で完璧に再現できるようにするための送信機能を搭載。
読み込んでいるVRMのパス情報を送信するようになったため、EVMC4Uなど対応している環境ではVRMを自動読み込みするようになりました。

また、背景色や照明状態などを送るので、EVMC4U側に送ることでほぼ完璧に同じ見た目を再現できます。(使っているアプリがどれだけあるかわかりませんが)
まあオプション文字列とかほぼ使われてませんね。

合わせて、コントローラをトラッカーとして認識させる機能が追加されています。OpenVRからのコントローラ取得処理に割り込んで、トラッカーとして認識させます。これにより、コントローラ補正処理をバイパスするため、コントローラを特殊な持ち方をしていたりトラッカー代わりに使用している場合でも使いやすくなります。

#37 OSC機能拡張のUIを追加、幾つかのバグ修正 (2020/1)

OpenVRが起動してないときにエラーになるバグを修正したのと、クリック透過がセーブされないバグ、大量の謎ログが出る問題を修正。

また、このあたりからGUI(WPF側)をいじり始めました。
PipeCommandsと、WPF(XAML)のいじり方を覚えて、UIが追加できるようになりました。

なので初めて載せたUIは、オプション文字列関連と、コントローラのトラッカー化対応ということになります。

#38 リモートキャリブレーション・設定切り替えの追加 (2020/1)

外部アプリからキャリブレーションをトリガーすることができる機能、キャリブレーションステータス情報、設定ファイルの情報の送受信をする機能を追加。

これにより、外部アプリから着替え(設定には読み込むVRMファイルを含むため)とキャリブレーションを行うことができるようになります。

#39 設定ファイルロード処理の整理と初期ロード、WPFのファイルダイアログ初期パス記憶 (2020/1)
#40 WPF: 存在しないパスが前回パスになっているときの例外問題への対処 (2020/2)

設定ファイルの読み込み処理を整理し、起動時に最後に読み込んだ設定ファイルを自動読み込みするようになりました。
また、VRMファイルなどの読み込み時のファイルダイアログの初期パスが毎回リセットされないようになりました。

また、その実装が安直すぎて前回のフォルダが存在しないときに落ちるようになったため、修正。

VMCProtocol公式サイト設立

ばもきゃと、EVMC4Uの2アプリ間だったプロトコル仕様(当時から公開)を、当時の様々な情勢と関連アプリの増加に伴い、専用ページを作って公開しました。


#43 トラッキング状態に応じたIK Weightの自動操作(既定で無効)とバグ修正 (2020/5)

トラッキング状態に合わせたIK Weightの自動調整(トラッカーの電池が切れたときとかに、自然とそのトラッカーを除外する機能)を実装。
ただし、調整が難しいのもあり現在release版では無効化されています。(ずっと)

また、ゲームコントローラや使ってないトラッカーに自動認識が吸われる問題や、Audio Listenerが邪魔など、諸々の不具合に対処しています。

#44 左右コントローラーのボタンが入れ替わっちゃう問題への対処 (2020/6)

OpenVRのコントローラの左右認識と、ユーザーがアバターで指定している左右の認識が逆になることがあります。
というのも、双方独立して認識されるもののためです。

これで困るのが、特に、VIVEコントローラのような左右の区別がないコントローラを使っていて、かつ、HMDを被っていない(頭トラッカーなど)の時に、手が勝手に入れ替わったり意図しない判定にされると、表情操作などボタン操作が入れ替わるのが困るので、その対応です。

なお、VRゲームの方は入れ替わったままなので、主にばもきゃ単体利用時が対象です。

#45 Unity LogのWPF画面出力機能、デザイン調整、ヘルプタブの追加 (2020/6)

個人的に好きなプルリクエストの上位に入るものです。

WPF画面側に、Unityのエラーログを表示できる仕組みを搭載。今までユーザーが内部エラーを知る手段がなかったのが、一気にわかりやすくなりました。
この機能は、拙作Oredayo4Vに搭載した機能の移植になります。

さらに、エラー表示をダブルクリックすることでトレース情報をクリップボードにコピーできるため、不具合報告がやりやすくなっています。
また、異常が大量発生しているとき、PCが重くて動かなくなるのを防止するため、エラーが1万回出ると強制終了するフェールセーフを入れました。
(毎フレーム出ると約3分で落ちます。これは、通常操作レベルで落ちないようにするためにこの数にしています)

ついでに、ヘルプ画面が追加されています。

#46 新しい設定項目の翻訳対応 (2020/6)

直近に追加された新規機能の名称が英語表記のみになっていたため、ダイナミックリソース化対応し、日・英対応を実施しました。
もともと、ばもきゃ内に多言語化の仕組みがあるのですが、私が機能と適当UIばっかり突っ込むので対応が追いついておらず。以後私が追加する機能も、基本的に多言語対応してから出すようにしました。

#47 VMCProtocol v2.6 Thru info対応(2020/6)

VMCProtocol v2.6 Thru infoに対応
いわゆる横流し命令ですが、使われているかは…

#50 VMTによる仮想カメラトラッカー機能の追加(2020/8)
#52 VMT追加実装対応

仮想トラッキングデバイスのVMTに対応する対応です。

もともとVirtualMotionTrackerは、ばもきゃのMR撮影のための仮想カメラトラッカー機能が外部ソフトウェア(LIV)の機能依存であったため、独立して自由に制御可能な仮想カメラトラッカーが使えないかということで生まれました。
(※MR撮影は、元々一眼レフカメラにトラッカーを取り付けて現実と混合撮影するSteamVRの機能がベースになっています)

なお、このプルリクは最低限の機能であり、実際にはあきら氏の方でインストールやルーム設定など様々機能が追加されて現在の統合された形となっています。

#54 ディレクショナルライトの外部制御対応 (2020/9)

外部からライト色を制御できるようになります。これにより、DJ的なこととかをする際に、きれいに連動できるかもしれません。(使っているところはまだ知りません)

#55 PostProcessing機能の追加 (2020/12)

PostProcessingStack v2を導入し、Oredayo4V相当のポストプロセッシングオプションを追加したものです。
下図のように、光効果やレンズ効果などを入れ、従来の一段上の表現が可能になります。(ただしグリーンバック合成などとは相性が悪いです)
また、環境光をオフにすることもできます。


#61 デバイス探索対応 (2020/12)

EDDP(独自デバイス探索プロトコル)に対応させるものです。
端的に言ってwaidayoと自動接続するための仕組みです。
逆に、EDDPに対応した受信アプリを待ち受けする機能もついています。

#83 ショートカットキーの拡張 (2021/8)

トラッキング停止や、写真撮影画面のショートカット機能を実装したものです。

#89 自動視線制御アルゴリズムの実装 (2022/12)

主に視線センサーを搭載していない環境向けで、アバターがカメラの方をチラみするようになったり、視線に微細な人間らしい揺れが追加されるアルゴリズムの実装と、意図的に目線をアバターの前方、顔の前、カメラ目線と切り替えできるようにしました。
これにより、生き生きとした挙動を実現できます。

#91 UI改善 (2022/12)

mocopi (2023/1/20発売予定) 公式対応ソフトとしてバーチャルモーションキャプチャーが登場したため、ユーザビリティ改善を目的としたプルリクエスト。

エラー落ち時に原因がわかるようになり、カメラの切り替えをわかりやすくなり、キャリブレーション操作が早すぎると見た目が変になる問題に対処。
他にも、よくある問い合わせで問題になるSteamVR ダッシュボードの表示中に警告を出すようにしたり、キャリブレーション完了時に詳細情報が出るようになります。

ばもきゃの内部構造について

今までいじったり貢献した経験からの、今後ばもきゃをいじりたい人、および、貢献したい人向けの雑多な解説です。

注意: これは勝手に解説しているもので、不完全性を含みます。
 また、開発に合わせて変化していきますので、詳細はソースを読み、原作者と会話してください。

Tips

  • 無償版には一部機能に制限があります。フル機能(先行リリース)版をダウンロードするにはpixivFANBOX(300円~)の登録が必要です。

  • ソースコードには下記の非公開SDK以外の機能はすべて実装されているため、pixivFANBOXから有料登録しなくても自前でビルドすれば、使用できますが、ビルドにはFinal IK(有料アセット: 記事公開時$99)が必要になります。

  • VRoid Hubなど提供元から非公開のSDKが使用されている部分はソースコードが分離されており、いじれません(ビルド対象外になります)。

  • Vive SR SDKや、Tobii Unity SDKなど利用するユーザーが限られているものに関しては、導入しなくてもビルドできる様になっています。

システム構成

まず、概ね以下の構成になっています。
[WPF] - [UnityMemoryMappedFile] - [Unity]

*Unity
アバターの表示
・VRMの読み込み
・リップシンク用の音声入力とリアルタイム音声解析
・OpenVRやその他情報取得システムとのやり取り(トラッキング情報など)
・Final IKと独自のアルゴリズムによる姿勢推定とキャリブレーション
・MIDI受信
・OSC (VMCProtocol)の送受信
・UDPデバイス探索
・設定ファイルの読み書き
・高解像度画像撮影
・仮想Webカメラ
・仮想カメラトラッカー制御
・VRoid Hub等との外部サービス通信
・MOD機能

*WPF (C#)
・ユーザーとの対話UI
・多言語対応リソース

*UnityMemoryMappedFile (C# DLL)
・UnityとWPFの間の通信 (PipeCommands.cs)

基本思想

  • 配信事故の防止の為、Unity側にはアバターと背景以外のものは一切表示しない。

  • UIはWPF側ですべて実現する。UI以外の機能はUnityで基本実現する。

  • 不要な音は自発的に鳴らさない。

  • 一度設定すると変更しないような機能は詳細設定に入れる

改造の流れ

改造する時によく行う流れを以下に説明します。
殆どの場合、Unity側に機能を追加したいというとこから始まると思われるので、その前提で解説します。

*目的の機能をUnityで実現する
Unity側の機能機の起点になるのは、WPFとの通信の起点の(Server_Receivedが実装されている)MonoBehaviourです。
そのため、この処理を追いかけることになります。

Server_Receivedが実装されているMonoBehaviourは現時点で以下です。
・ControlWPFWindow (ほぼすべての処理の起点)
・CameraManager (カメラ制御)
・InputManager (キー操作やコントローラボタン操作、MIDI入力など)
・MocopiConnector(mocopi)

また、VMCProtocolとの界面は以下です。
・ExternalSender
・ExternalReceiverForVMC

現在読み込まれているアバターの情報や、通信、大抵の(シングルトンでない)クラスやGameObjectの情報はControlWPFWindowに登録されています。
また、モデルの読み込みなどの各種イベント発生時の処理は、VMCEventsに定義されています。

そのため自作のスクリプトを作る場合、まず最初にStart()にて、下記のように捕まえておくことが多いです。

window = GameObject.Find("ControlWPFWindow").GetComponent<ControlWPFWindow>();
VMCEvents.OnModelLoaded += (GameObject CurrentModel) =>
{
    if (CurrentModel != null)
    {
        this.CurrentModel = CurrentModel;
        animator = CurrentModel.GetComponent<Animator>();
        vrik = CurrentModel.GetComponent<VRIK>();
        blendShapeProxy = CurrentModel.GetComponent<VRMBlendShapeProxy>();
    }
};

*UIを作る
UIを作るには、WPF側プロジェクトを開きます。
MainWindow.xamlか、SettingWindow.xamlをいじることが多いです。
普通のWPFアプリ開発なので省略

UIを多言語化するには、Resources\XXXXX.xaml に定義します。
その上で、xaml上であれば
 {DynamicResource 定義名}
C#コード上であれば
 LanguageSelector.Get("定義名")
を使います。

*通信を定義する
UnityとWPFの間を連動させるために、送受信用のclassを定義する必要があります。
ここまでで通信に必要な内容は決まっていると思いますので、WPF側ソリューションのPipeCommands.csを開いてclassを追加します。
WPFからの送信(Set)・受信(Get)用で分けても良いですし、共通にしても良いようです。
定義したらソリューションをビルドすることで、自動でDLLがコピーされUnity側でも定義が使用できるようになります。

Unity側のServer_Received、WPF側のClient_Receivedに受信処理を定義します。
送信処理は、SendCommandAsyncを使います。(なお非同期処理のためcontext.Postが必要です。)

注意: 単なる設定UIと考えると、WPF→Unityの方向だけ実装しがちですが、設定ファイルの読み込みがあるため、必ずUnity→WPFの向きで受け渡しする機能を実装してください。

*設定ファイルに定義する
ここまでで機能としては十二分に動作する状態になったと思いますが、常時動作の場合や、自分用の機能でなければ、大抵の設定を記憶しないと使いものにならないと思います。

設定ファイルの定義は、Unity側のSettings.csにあります。

  • まずSettingsに[OptionalField]をつけて、設定項目を定義します。

  • SettingsのOnDeserializingMethodに、初期値を実装します。

  • ControlWPFWindowのApplySettingsにて、Settings.CurrentをWPFや各コンポーネントに反映する処理および通信処理を実装します。(設定ロード後処理)

  • ControlWPFWindowのServer_Receivedあるいは、各コンポーネントに、Settings.Currentに値を保存する処理を実装します。(設定セーブ前処理)

これで、設定が保存されるようになります。

これで内部構造の雑解説は終わりです。
最後に、いきなりソースを投げつけるより、先に原作者と会話しておくことをおすすめします。