読者です 読者をやめる 読者になる 読者になる

Tutti Lab

元シリコンバレー在住のおっさん技術者、モバイルVRアプリ開発に挑戦中

Cardboardで360度立体視動画ビューワーを作る(4)

はじめに

過去3回に渡り、メーカーフェアー出展向けということで、360度立体視動画ビューワーを開発しました。前回は、Theta2台で撮影した360度映像をOculus CV1で再生する、というものを開発しました。これにより、Thetaから取り込んだ右目・左目用の360度動画をそれぞれ右目・左目にリアルタイム表示できるため「まるで自分がThetaの設置場所にいるような」感覚を作り出せます。
ただし、メーカーフェアー向けの出展物として考えると、Oculus1台だと見に来られたお客様の一部にしかVR体験をしてもらえませんし、また当初考えていた「Thetaをミニスタジアム内で動かす」ことにより作られた映像は思ったよりオモシロイものではありませんでした。
このような背景から出展物のコンセプトであった「Theta2台で撮影した映像をリアルタイムで閲覧」の「リアルタイム」をあきらめ、その代わりにVR(ある種ARっぽいですが)の特徴を活かし、CGとTheta撮影映像を合成して、自分だけのオモシロVR映像を作成し、その場で見れる・家族や友達に見せびらかせる、というものにしました。結果としてこのコンセプト変更はとてもうまく行き、メーカーフェアー期間中は、たくさんの子どもたちが当ブースでオモシロVRムービーを撮影、閲覧して楽しんでいってくれました。
以下では、本コンセプト変更により開発した「Theta映像とCGを重畳したムービークリップの作成と録画」について書いてみようと思います。
f:id:tuti107:20160530024145j:plain

Theta二台から立体視用Equirectangular映像をつくる

前回ご紹介したTheta Shader Packを利用して、上下にEquirectangular形式のTheta映像を並べてみます。なお、後ろ側については、視差が逆転してしまう、展示においても撮影者(=私)が映っているだけで価値が低いとの理由から、全面180度分のみを利用します。
f:id:tuti107:20160530030235p:plain
まずはCameraの設定ですが、以下のとおりとします。
f:id:tuti107:20160530030354p:plain
ポイントはProjectionをOrthographicとしている点です。こうすることで、Z座標を大きくしても表示の前後関係には影響しますが、遠近法で小さく表示されることはありません。今回は2つのQuad縦に並べて表示するだけであり、表示物に遠近感をつける必要がないため、このような設定としています。
左右Theta映像を映すQuadについては、以下のとおりです。表示位置とカメラ映像のインデックス(CamIndex)以外は同じ内容です。
f:id:tuti107:20160530030841p:plain

f:id:tuti107:20160530030848p:plain
ここまでは、前回とほぼ同様(Theta映像の貼り付け対象がSphere→Quad)です。なお、上記の通り360度全体ではなく、全面180度のみの利用にしておりますので、MaterialはThetaEquiRectangular(Forward)に変更しました。

CGと重畳する

次は、上記Theta映像の透過処理と、背景CGの作成です。まず、Theta映像の透過については、上記Materialに設定されているシェーダー用コード「ThetaRealtimeEquirectangular.shader」を一部修正し、緑色は透過とするようにします(いわゆるグリーンバックのクロマキー処理です)。なお、本ソースコードについては私の著作物ではありませんので、改修をした部分のみ、下記に記載します。

	SubShader
	{
		//Tags{ "RenderType" = "Overlay" "Queue" = "Overlay" "ForceNoShadowCasting" = "True" }
		Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" "ForceNoShadowCasting" = "True" }

		//ZTest Always
		Cull Off
		//ZWrite Off
		ZWrite On
		Blend SrcAlpha OneMinusSrcAlpha

まずは、描画順序の設定です。元々(コメント部)を非コメント部の内容に変更することで、背面にあるオブジェクトとTheta映像を映すQuadが重畳表示されます。詳細についてはシェーダ用コードの解説サイトをご参照いただければと思いますが、ざっくり、上記とすることでQuadの背面にあるオブジェクトも描画され、さらにQuadと重ねあわせた(透過部には背面のオブジェクトが表示)形式になります。
次に、frag(v2f i)関数の末尾に以下のクロマキー処理を追加します。

	half maxCol = col.r > col.g ? col.r : col.g > col.b ? col.g : col.b;
	half rateR = col.r / maxCol;
	half rateG = col.g / maxCol;
	half rateB = col.b / maxCol;
	if (rateG >= 0.9 && rateR < 0.8) {
	  col.a = 0.0;
	}

ピクセルの色が緑の場合は、アルファを0にして非表示とする、それだけの処理です。緑の判定は若干意味不明かもしれませんが、環境光等を考慮していろいろ試行錯誤した結果、このような条件となりました(最適、とは思っていません。。)。
次に背景CGについてです。背景CGについては「普通に」Unityシーン内にオブジェクトを設置し、Skyboxを設定して作成するのですが、最後にこのシーンを360度EquiRectangular形式に展開する必要があります。今回はDomeMaster Texture Generator for Unity利用しました。これを利用することで、シーン内に配置したオブジェクト群からFisheyeまたはEquirectangular映像を生成することができます。まずは上記をダウンロードし、DomeMaster.csと、Equirectangular.shaderをプロジェクトに取り込んでください。その後、適当なMaterialを作成し、シェーダーをEquirectanglarとしてください(私はER.matとしました)。
このマテリアルをはりつけたQuadを以下のとおり2つ生成します。
f:id:tuti107:20160530062639p:plain

f:id:tuti107:20160530062646p:plain
差異はY座標だけです。
つぎに実際に重畳する風景を作成します。私は、Moon Environmentというアセットを使いました。有料($30)ですが、リアルな月面風景を実現できるので、(そのような用途がある方に限りますが)おすすめです。
この風景は、適当に作成したEmpty Object配下に生成し、最後にレイヤーを設定してください。この背景が、以下で説明する背景のEquirectangular化以外のカメラに写り込まないようにするためです。
f:id:tuti107:20160530063431p:plain
またこの背景は、原点から遠い場所に設置するほうがよいです。実行時には特に影響はありませんが、エディット時にこの背景が原点付近にあると結構邪魔です。
最後に、背景をEquirectangular化するためのスクリプトを設置します。背景の中心に適当なカメラを設置し、Add Component→Dome Masterでスクリプトを追加します。さらに、カメラのCulling Maskを上記作成した背景のレイヤーと一致させます。これだけで、上記で設置した2つのQuadにEquirectangular形式の背景が描画されるようになります。
f:id:tuti107:20160530064313p:plain
これは、PCのディスプレイ右下に緑色長方形のものをおいて撮影したものです。その部分が透過され背景の月面が見えています。
なお、上記では背景は単一カメラで生成したものを両眼用に設置しているため、立体表示されません。上記のDome Masterを配置したカメラを2つ置くことで立体視対応もできますが、いろいろと複雑な改造が必要となってしまうため、ここでは触れません。

録画する

上記で生成したTheta映像とCGを重畳したものをMP4形式にエンコードします。本エンコードにはffmpegを利用しました。このffmpegにはNVidia GPUを利用したエンコード(nvenc)のオプションがあり、一般的には非常に高負荷な処理であるMP4エンコードを高速実施することが可能となります。第二回でMAC+OBSでエンコードの負荷が高すぎて全然フレームレートが出ない件についてふれましたが、nvencを利用すればこのような心配は不要です。ただし、nvencオプションはデフォルトのWindows用バイナリでは無効化されており、有効化するためにはffmpegをビルドする必要があります。
ffmpegのビルド(Windows版)は、こちらを利用すれば、そこそこの手間でビルドが可能です。ただ、VirtualBoxをインストールしたり、そこにUbuntuをインストールしたり、それでビルドをしてもエラーが多発したり、とそれなりに敷居が高いです。
nvenc対応のffmpegのビルドができたら、次に以下のスクリプトを用意します。

gist4b9ea7a85dc70d8b1d0eff515a7168c8
このスクリプトを適当なGameObjectに貼り付け、以下の通りインスペクタより設定します。
f:id:tuti107:20160531064738p:plain
突貫で開発した、ということもあり、まずffmpegを起動し、毎フレーム、インスペクタにてDisp Cameraに指定されたカメラの内容をjpeg化して標準出力でffmpegへパイプ渡し、MP4へエンコードする、という手順をとっています。速度性能はいまいちであり、nvencでがんばった分を差し引いても、当方のPC環境で20fpsがやっと、という感じです。この辺りは今後もう少し見なおしていいきたいと考えています。
なお、video Dirは、エンコードしたMP4動画を配置するディレクトリを指定します。ファイル名は生成時間で自動生成しています。

まとめ

これまで4回に渡り、メーカーフェア出展に向けた360度立体視動画ビューワ(+レコードアプリ)を開発してきました。開発期間が短く、かなり突貫実装となってしまいましたが、それでもたくさんの方(特にキッズ)に使ってもらい、喜んでもらえたというのは、開発者冥利につきます。
本件は、今後もブラッシュアップを続け、できればメーカーフェア東京にも出展したい、と考えております。本ブログを見ている方とメーカーフェア東京で出会えたら、非常に嬉しいです!