ネットワーク間頂点ストリームのためのデータ簡易圧縮とWebRTC通信【突発ネタ】

やったこと,でききたことを三行で

  • Unityのスキンメッシュや座標移動変換後の頂点をストリーム化
  • 座標情報を生データの半分以下に圧縮,受信先でも展開しやすいコンテナ化の構造を作った
  • WebRTCを経由して頂点ストリームをスマホ程度の回線でも見れた

経緯

動画を使って3Dモデルを360度から眺められないかという相談をうけ,また同じ時期にツイートをした経緯から,
メガデモ研究のため頂点圧縮の手法について考案してたことをひっくり返して再度作り直しました.今回は成功例?としての紹介になると思います.

動画で構造データの圧縮は精度が厳しい

i-saintさんからのツイートが作る切っ掛けになってたんですが,実は同じようなことをして失敗?した経緯があります.
失敗話から,以前の記事「ハコスコやCardboardを利用したVRアプリを開発しています。【終了しました】」でデプス情報を元に三次元の構造データを復帰させることを練っていたんですが,高解像度化すればするほど,奥行きの精度が足りなくなって,ピクセルデータに対して不均一なノイズや平滑化がかかって,どうやってもhalf floatの精度もでませんでした.動画は動画の圧縮手法でしかないと痛感しました.
また,iPhoneやAndroid上で動作させるつもりだったんですが,動画から静止画の切り出しって,処理重いんですよね…動画の再生自体はハードウェアエンコードできても,毎フレームごとに静止画としての切り出しってそれだけでCPUに負荷がかかる上,さらにテクスチャとして転送,ジオメトリ変形とやってたら,どうやってもスペック不足に陥りました…
この苦労の残骸はGitHubにアップロードしています.

ストリーム化の指針とコンテナ化

以前の失敗をバネに,劣化にそこそこ強い非可逆の圧縮形式を再度考えたところ,特定領域をブロック化してその中で固定精度を保つ考えに至りました.
この方法の発想はこれら研究がアイデアになってます.
Tiled ShadingやClusterd Shading は 無数のライト情報を効率よく計算するために,ライトの影響範囲をブロックとして考える手法で,
ブロック化した後は,影響のない範囲に関しては計算が少ないか,間引けるという発想で,構造データを圧縮する理想的な考え方でした.
コンテナ化と圧縮のステップについてはこんな感じです.

  • 頂点の有効範囲を取り出す(カメラ座標系でのモデル頂点xyz各要素の範囲を-2~2までに限定する,など)
  • 範囲内をボクセルブロックとして扱い,メッシュをまたがって特定のボクセルをマージする(内部でインデックスは作っておく)
  • 1ボクセル内の頂点の数値を範囲内を0~255にアライン,ボクセルブロックも対象範囲内で32, 64, 128という単位でアライン
  • 1ボクセルごとにコンテナ化,頂点情報入っていないボクセルは間引く
  • コンテナを結合して1フレーム分作成

20161025_img1

ボクセル化のイメージ

© Unity Technologies Japan/UCL

20161025img2

コンテナ化された頂点情報

コンテナは最低限の記述で,ボクセル領域のID,ボクセル内の頂点数が入れ物情報で,中身は,頂点インデックス情報とxyzの頂点の成分を入れてます.

デコードはこの逆をたどればできるので,構造化したバイナリデータがちゃんと受信できれば処理負荷は少ないです.

変化しない情報は別の通信にまわす

まだ実装できていませんが,ポリゴンインデックスやUV座標,マテリアル,テクスチャ情報などは,相当特殊なことをしない限りは変化しないので,静的に通信できるWWWクラスなどを使って通信するのがよさそうです.現在ポリゴンインデックスは頂点情報をストリームする前に取得するようにしています.

データストリーム,WebRTCの話

Unityからでは,対戦通信用のクラスやWWWクラスが使えるので,量の少ない動的データや静的なデータは扱えますが,量の多い動的なデータを取り扱うクラスがないため,外部ライブラリ頼りになりました.
最初は下の動画のようにUDPを使ってたんですが,送信側は接続先知らないと送信できないので,将来的に面倒になりそうなのと,出力先がUnityであるとは限らないので,今回は今後の汎用性を高めるためWebRTCを使いました.Unityからは「WebRTC Network – Unity Asset Store」使いました.無料のうえ,node.jsサーバとも相性が良かったので楽ができました.
アセットでRTCDataChannelのAPIが公開されていた(チャットサンプルがRTCDataChannelを使ってた)ので,サンプルの中身をほぼコピペでバイナリの送受信ができるようになりました.
動作デモの映像は以下のツイートから,

© Unity Technologies Japan/UCL

これやる前にUDP経由でうごかしたのはこちら

© Unity Technologies Japan/UCL

ネットにデモがアップできればいいんですが,ホスト側がUnityの実行環境が必要になっている上,
WebRTCという枠組みで作っているため,用意するのに時間がかかりそうです.

シグナリングとか

シグナリングはさくらVPSでサーバーを立てました.これもアセットのサンプルに含まれてて,一切の書き換えなしに動いたのでアセット様様です.
さくらvpsとnode.js周りはこちらの記事「さくらVPSを契約してCentOS6.4をNode.js Webアプリケーションサーバーとして構築するチュートリアル – Qiita」を参考に.

まとめ

UnityちゃんSDは等身に対して結構ポリゴン数が多いですが,1フレームごとの通信料が50KB以下と,結構圧縮できてて,型落ちのデスクトップでも30fpsのデータを送れているため,今回はうまくできたと思います.
今後はマテリアルやテクスチャを簡単に転送したり,ダウンロードする仕組みを作って,アセット化したいところです.