概要
こんにちは!
VRChatを中心にXR関係の活動をしているサックーと申します。
この記事は、NVIDIAが公開した高速なNeRF技術であるInstant-NGPに360度動画を素材として使うことで、楽に3Dシーンの再現ができた話です。
新しい(?)部分としては、Unityを使って360度動画から2D画像を切り出しているところかなと思います。
また、これはIwakenLabアドベントカレンダー6日目の記事です
使用技術
- Unity 2021.3.4f1 BRP
撮影機材
- insta360 X3
ビルドを公開しました!(2023/3/18追記)
こちらからダウンロードしてお使いください。
きっかけ
さて、件のリポジトリ公開後Twitterでいろんな人がInstant-NGPを試しているのを見て、自分もやってみたところ想像以上に簡単に高精度なものができて普通に感動していました。
しかし自分は登山が好きでして、そういった自然の大きな空間を3Dで再現したいなあと思ったのですが、そういった場所で大量の写真を撮るのは大変ですし山域によっては危険です。
そこで、動画からも画像を切り出して素材として使えると見たのでやってみたのがこちらです。
過去に適当に撮った動画からこういった3D空間際限ができるのは、すごい可能性を感じました。去年撮った山の動画からこうなった
— サックー@XR (@VRC_Sakk) September 27, 2022
4K映像だったらもう少しきれいになるだろうか
とんでもない技術だ… pic.twitter.com/YlnSxxrjKg
しかしこれもまだ弱点があります。
通常の動画では、歩きながら撮った場合に正面しか映らないため、出来上がる空間は歩いたのと同じ方向からしか見ることができないものになってしまうのです。
そこでついに登場するのが360度動画です。360度動画であれば全方向の見え方をカバーできるのではないかと。
検索してみたところ、すでに先駆者の方がいらっしゃいました。
フォトグラメトリやシェーダーなどを中心にXR領域で活躍されているVoxelKeiさんです。
360度動画から画像を切り出して適用することできれいに再現できていることが分かります。 ただその切り出しに使えるツールがあったわけでは無いようでした。 そこで自分でそんなツールを作ろうと思い立ち今回の件につながるわけです。360度素材からの90度切り出し法でInstant NeRF出来た。撮影はinsta360 one X2。次は1inch版で試す。 pic.twitter.com/u030srczC8
— VoxelKei (@VoxelKei) July 21, 2022
基本の考え
技術的にすごいことは何もなく、素朴な実装をしています。
- 360度動画をRenderTextureに流す。
- SkyboxのMaterialにそのRenderTextureを設定する。
- シーンにカメラを4つ90度ずつ配置する。
- 再生しながら等間隔でカメラの画をpngに保存する
とはいえそこまですんなりいかなかったとこもあり、詳細を書いていきます。
詳細
Skyboxで360度動画を再生する
ここはすんなりいけました。
RenderTextureのSizeに映像の解像度を指定するくらいでしょうか。
Skybox用のMaterialのShaderはSkybox/Panoramicを使います
これをSkyboxにしてシーンを再生すれば全天球で動画が再生されます。
指定した間隔で画像を保存する
これが割と難敵でした。
カメラの画をpng保存する方法はこちらなどを参考にしましたが、どうやってもこの処理は重いです。
そしてそれを4つのカメラで同時にやるのでとても重いです。
最初は非同期処理で並列してやれば軽いのではと思いましたが、それぞれのカメラでキャプチャするタイミングがずれるので断念しました…
一番の問題は動作が重すぎて、動画の再生フレームとスクリプトの実行フレームがぐちゃぐちゃになり、等間隔での撮影ができないことでした。
そこで利用したのがVideoPlayer.frameReadyです。
これはVideoPlayerで各フレームの描画準備ができたときに発火するイベントになっています。
ただしこれ
videoPlayer.sendFrameReadyEvents = true;
をしないと発火しないので注意です。
あとは指定したフレームレートでキャプチャをできるようにして完成しました。
ただこれでも動作が怪しかったので、結局動画の再生速度を0.2倍にしてどうにかしました…
カメラのFOVの設定機能
個々のカメラの水平視野角を設定できる機能も付けました。
エディタのInspectorでは水平の視野角を直接指定できますが、スクリプトからだと垂直視野角しか設定できないので変換してやる必要がありました。
こちらの記事を参考にさせていただきました。
できたもの
- 画像書き出し先指定(直書きですが)
- 切り出し元動画指定
- キャプチャフレームレート指定
- カメラの水平視野角の指定
- 使用カメラの指定(一応何個でもできる)
があります。これを使うことで、360度動画からこのような連番画像を入手できました。
そしてこの画像から再現したのがこちらです。
元動画を見てもらえばわかるのですが、録画しながら普通に歩いただけのものからこうして再現ができました。 もちろん1枚1枚写真を撮ってやったほうがきれいにはなりますが、お手軽さはすごいと思います。ここの360度動画からNeRFできた!
— サックー@XR (@VRC_Sakk) October 22, 2022
どうやら徒歩スピードだと2fpsくらいでの切り出しが必要っぽいことが分かってきた https://t.co/ma1YoeMSXb pic.twitter.com/Va6npSlhVU
おまけ なぜカメラのFOV設定機能を付けたか
なぜカメラの水平視野角を設定する必要があるのかというと、この値が最終的な3Dシーンの精度に影響するからです。
最初単純に90度で切り出したところ、カメラ位置の推定がうまくいかず、同一店からの撮影として認識されませんでした。
これは画像間の重なりが一切なかったためと思われます。
そこで120度でやってみたところうまく推定してもらうことができました。
いくつか値を試すと95度でもうまくいき、120度の時よりきれいな気がしました。
広角にすればそれだけ絵もゆがむのでその影響もあるのかなと思います。
研究のしどころかもしれません。(他力本願)
おまけ2 撮影時のポイント
ただ歩いて撮るだけと言いましたが、おそらくジグザグに歩いたほうが良いと思います。
元々の前提として同じものを複数の方向から撮るというのがあるので、ジグザグに歩くことで多少角度を補うことができる気がします。
上で紹介したVoxelKeiさんの動画でもそうされているように見えます。
自分は意識してなかったのですが、山だと傾斜を緩くするためにジグザグ歩くので、それが幸いした可能性があります。
まとめ
広域のフォトグラとかすごいけど大変そうだなあなどと思ってしまうめんどくさがりでしたが、360度カメラとInstant-NGPによってお手軽に広い空間の3D再現ができました。
もし360度カメラと強いグラボをお持ちの方はぜひやってみてください!