2017/03/25 (土) に日本マイクロソフト品川本社にて開催された Tokyo HoloLens Meetup vol.2 に参加/登壇してきました。会場に行くと日本の HoloLens ブームの凄まじさに驚くばかりです。そんな圧倒的ギーク達を前に、僭越ながら HoloLens で最もクールな花形機能である「Sharing」について解説させていただきました。
Sharing 機能は僕自身がメインエンジニアとして開発している Project Sonata で実際に利用しており、開発に際しては相当苦労しましたw なので今回のセッションでは、その開発中に得た基本的な考え方やハマりポイントなど、現在日本語でも英語でも全然公開されていない知見を共有させていただきました。
Sharing はただでさえお高い HoloLens が最低 2 台必要ということで、日本でいくら HoloLens が加熱しているからと言って簡単に試せるものではありません。そういう点でも人柱としてガチでやってみた系の情報共有は、今後の HoloLens 開発に対する良い Tips になれたのではないかと思います。
大変ありがたいことにセッションの反応も想像以上に良く、セッション後には持って行った名刺が全部なくなるほどにたくさんの方々からお声掛けいただきました。こんなことは初めてだったので正直驚いていますw また、セッション中のツイートは Togetter にまとめられています。感謝×2 :)
Sharing のキモは座標系の共有
これに尽きます。座標系の共有ができないうちは同じ空間内での Sharing は「絶対に」完成しません。まず前提知識として HoloLens における座標系は以下のようになっています。
- アプリケーションを開始したときのヘッドセットの位置が原点
- 視線方向 : Z 軸正方向
- 向かって右側 : X 軸正方向
- 上 : Y 軸正方向
つまり、同じ室内にいたとしても A さんと B さんはそれぞれ独自の座標系を持っています。この状態で A さんが (x, y, z) = (1, 2, 3)
の位置にオブジェクトを生成したとして、これをネットワーク経由で B さんにも配置するように指示したとしても、B さんの座標系は全く別なので (x, y, z) = (1, 2, 3)
は全然別の場所になってしまいます。そこで WorldAnchor
を設定し、それを原点とした座標系を共有し合うことで、(x, y, z) = (1, 2, 3)
が同じ空間内で同一の場所を指すようにします。
座標系の共有は完全に下準備です。これに成功したら、共有座標系に対する localPosition
/ localRotation
でやり取りすることで、晴れて Sharing が完成します!
Sharing の実現手順
Sharing を行うまでの大まかな手順をまとめると以下のようになります。言うは易しなのですが、やってみると案外面倒なものです。
- 原点 (= 上図の Os) とする
GameObject
に対してWorldAnchor
コンポーネントを引っ付ける WorldAnchorTransferBatch
を使って先に設定した空間アンカーをシリアライズ- ネットワーク経由でシリアライズしたデータを全ユーザーに配信
- 受信したデータを
WorldAnchorTransferBatch
を使ってデシリアライズしWorldAnchor
を設定 - 原点となる
GameObject
の子要素としてオブジェクトを生成/配置 - オブジェクトの移動や姿勢変更があれば
localPosition
/localRotation
をネットワーク経由で共有
空間アンカーの前回値保持
空間アンカーの設置/同期にはかなりの時間がかかります。また、UWP アプリケーションはそのライフサイクルの仕様からバックグラウンドにいるときに自動で終了させられたりします。このようなことがあるので、アプリケーション再開時に高速で空間アンカーを復旧させることには一定の意味があります。ただし、空間アンカーを保存した部屋と読み込みする部屋が別だったりすると一体どこに復旧させたら良いか分からなくなるので、同一空間での利用なのかどうかと言ったところに気を配った実装をしなければならず、思った以上に手間です。前回値保持は Sharing を実装する上では必須機能ではないので、いっそのこと思い切って無視しても良い気もします。
空間アンカーの前回値保持には WorldAnchorStore
を使って行います。これはいわゆる KVS (Key Value Store : 辞書型ストレージ) として提供されています。
空間アンカーの共有におけるハマりポイント 6 選
冒頭の資料にあるものをピックアップしますが、メチャクチャ多いです。正直イヤになるくらいハマりますし、「もはや無理なのでは?」と挫折すると思います。これらをひとつひとつを忍耐強くクリアしていかないと、アプリにすることはおろか他人にデモを見せることすら難しいでしょう。中途半端に Sharing を搭載したアプリをリリースしたとしても「Sharing できないので ☆1」は普通にあり得る話だと思います。つまり、本当にツラいです...(ギャフン
1. 小さ過ぎるデータサイズ
シリアライズ結果が小さいとデシリアライズに失敗しやすようです。HoloToolkit-Unity のサンプルでは 100 KB を最低保証値としていて、コメントに「小さ過ぎると失敗しやすい傾向にある」とだけ書いてあります。そうらしいのでとりあえず信じましょう。
2. 滅多に成功しない保存/読み込み
まずまずシリアライズに全然成功しません。成功率は 10 % 未満なのではないかと思うほど成功しません。そして運よく成功したしたとしてもデシリアライズにも高頻度で失敗します。成功するまでリトライするような地道な実装が必要です。
3. 大き過ぎるデータサイズ
先ほどはデータサイズの最低保証値として 100 KB と書きましたが、実際には簡単に 10 MB を超えたりします。社内で実験していたときに 50 MB を超えたこともありました。それぐらいのデータ量になることもある、というのを知っておく必要があります。
4. バカにならないデータ転送量
空間アンカーを共有するにあたり、先に挙げたようなバカでかいデータを全ユーザーにブロードキャストする必要があります。が、当然カジュアルにやってしまっては非常に危険です。例えば海外でデモをしようとして、レンタル Wi-Fi (= 500 MB/day) などで通信するとすぐに上限に振り切って死にます。ですので、データサイズを減らしたり通信回数を減らすような実装上の工夫が多数必要になりますし、安定した Wi-Fi 環境が必須になります。
5. 保存/読み込みが超絶遅い
WorldAnchorTransferBatch
によるシリアライズ / デシリアライズは、それだけでそれぞれ 30 秒かかるとかザラにあります。さらに先に挙げたように高頻度で失敗するので「1 分以上待って失敗!」と言ったことも発生し得ます。圧倒的にイライラが募るので、心に余裕を持って作業に当たってくださいw
6. 圧倒的デバッガビリティの低さ
Sharing の検証を行うには実機を使って部屋中を動き回る必要があります。ですが、Visual Studio のデバッガーを使おうとすると PC にケーブルを繋いでいる必要があります。動きたいのに動き回るのが困難という現実に直面することになります。そして 2 台以上にデプロイして検証する必要があるので大変煩わしいです。さらにエミュレーターは Sharing においては何の役にも立ちません...。結局僕は printf debug 的に画面にメッセージを出すことで作業していました。
SharingService.exe の是非
みんな基本は Holographic Academy : Holograms 240 から Sharing の勉強を始めるので、そのサンプル中に出てくる SharingService.exe を利用するのは当然だと思いがちです。が、ShariingService.exe を利用する以上はそのメリット / デメリットを十分に把握しておく必要があります。
メリット
- Sharing はもちろん、音声通信など豊富な機能が用意されている
- クライアント実装だけに集中して開発できる
デメリット
- カスタマイズ性が一切なく、ボトルネックになった際に対処不能
- スケールアウトできないので少人数利用に限られる
- 自前 API がある場合、それと異なる通信の口を持つことになる
僕は Project Sonata を開発するにあたり「SharingService.exe は自由度がないのでナシ」という判断をし、MagicOnion という弊社 CTO (@neuecc) 謹製の gRPC をベースとしたハイパフォーマンス通信フレームワークを選択しました。これは相当な茨の道でしたが、大きなカスタマイズ性とパフォーマンス、そしてすべて自分で制御しきっているという安心感を手に入れました。また、Sharing の通信部分を完全に自作したことで Sharing 自体に相当詳しくなったという点でも良かったです #今となってはw
まとめ
Sharing は上手くできると本当に未来を感じます。メッチャ夢があります。でも、全然簡単じゃない。これだけは覚えておいてください。