Unity 2018.1 でステレオ VR 動画を作成する

## はじめに

続き → [Unity でステレオ VR 動画を作成する (ほぼ完全 (?) 版)](https://qiita.com/tan-y/items/941de5c8bc3309f835d5)

Unity 2018.1 では標準で VR 用の動画ファイルを作成する機能が実装されてきたようですが、ちょっと問題があって使うのは厳しいかなーという感じでしたので、その辺含めて現状での VR 動画の作成の仕方を解説していこうと思います。  
equirectangular の映像を作るアセットは以前からいろいろあったので今更感もありますが、標準機能と Unity 謹製のアセットで可能になってきたのは大きいと思います。長い記事になってしまいましたがお付き合いいただければと思います。

Unity のバージョンは Unity 2018.1.0b2 (b3) ですが、まだ初期ベータですのでこれからどんどん変わる可能性もありますので、その辺はご了承ください。

きっかけと参考になった記事はこちらです。ありがとうございます。

[[Unity] Unity 2018.1 ベータから利用可能になった360°動画撮影(recording)を行う方法](http://d.hatena.ne.jp/nakamura001/20180112/1515744861)

今回作成したスクリプトで試しに [Candy Rock Star をソースにステレオ全天球動画を作ってみました](https://www.youtube.com/watch?v=c6eD2zrdlVU) (ステレオ VR での視聴は YouTube VR 等で。ブラウザでは通常のパノラマ動画) が、この動画のように "カメラが任意の向きに変えながら動く動画" は少なくとも Unity 2018.1b2 (b3) + Recorder アセットの 360 View 出力では実質作成ができないと思います。

[![[3D 360 VR] Unity-Chan "Candy Rock Star" Stereo VR Movie Test](http://img.youtube.com/vi/c6eD2zrdlVU/0.jpg)  
[3D 360 VR] Unity-Chan "Candy Rock Star" Stereo VR Movie Test - YouTube](https://www.youtube.com/watch?v=c6eD2zrdlVU)

© Unity Technologies Japan/UCL

※YouTube VR でうまく検索できない場合、 ID の "c6eD2zrdlVU" で検索してください。

## VR 動画ファイルとは

詳細は割愛しますが、ここでは全天球映像を "正距円筒図法 (equirectangular)" で平面化した映像を動画として記録したものを扱います。ステレオ VR 動画では左右の眼用に個別の映像を記録します。

## Unity 2018.1 での VR 動画作成向け新機能

Unity 2018.1b2 で下記の機能が使えます。

* Camera.RenderToCubemap のオーバーロードバリエーションにステレオ映像作成のためにレンダリングする映像の「目」のタイプを指定できるようになりました。
* Cubemap テクスチャから equirectangular 形式の映像をレンダリングする "RenderTexture.ConvertToEquirect" メソッドが新設。

また、

* 上記 2 つの機能を利用して Unity 製の録画アセット "Recorder" がステレオ全天球動画作成機能を実装。

となっています。

* [Camera.RenderToCubemap](https://docs.unity3d.com/ja/2018.1/ScriptReference/Camera.RenderToCubemap.html)
* [RenderTexture.ConvertToEquirect](https://docs.unity3d.com/ja/2018.1/ScriptReference/RenderTexture.ConvertToEquirect.html)
* [Recorder](https://assetstore.unity.com/packages/essentials/beta-projects/recorder-94079)

## 新機能の問題点

これらの新機能を実際に使ってみるといくつか問題があることに気づきました。

### RenderTexture.ConvertToEquirect の映像出力先が間違っている (修正済)

多分 Unity 2018.1b2 のバグだと思います (b3 でも未改善) が、左目用の映像が下半分に、右目用の映像が上半分に出力されるようです。標準的には左目用を上にしないといけないので多くのプレイヤーで正しく描画されなくなります。まあこれは逆のパラメーターを指定すれば回避できるのでそこまで問題ではないですが、 Recorder アセットの機能で直接全天球動画を作成すると正しくないものになることになります。

* [再現プロジェクト](https://github.com/aosoft/UnityConvertToEquirect) -  ~~Unity 2018.2f2 までは改善していないようです。~~ Unity 2018.3b1 で修正されました！

### 任意の方向を正面にできない

※Unity 2018.2.3f1 では直ってた (?) のですが、 2018.3b1 では戻ったようです。

これがおそらく致命的。

とりあえず再現してみましょう。下記手順をやってみてください。

1. GitHub から [サンプルのプロジェクト](https://github.com/aosoft/UnityVRVideoRecordingTest/releases/tag/20180121) を checkout して、 Unity 2018 でプロジェクトを開いてください。
2. Asset Store から "Recorder" アセットをプロジェクトに追加してください。
3. シーンファイル "Assets/main.unity" を開きます。
4. メニュー "Tools - Recorder - Video" で Recorder アセットの設定パネルが表示されるので、 下記のように変更します。
    * "Collection method" を "360 View" に切り替える。
    * "Output width" を 2048, "Output height" を 1024 にする。 
    * "Render in Stereo" のチェックを外す。
5.  "Start Recording" で録画します。

既定ではプロジェクトフォルダーの下の "Recordings" フォルダーに出力されます。出力された MP4 ファイルを再生してみると・・・

![8.png](https://qiita-image-store.s3.amazonaws.com/0/211064/31f2b102-a245-9c67-2ad6-c9d5d189620b.png)

何かオブジェクトが見にくい位置に描画されていますね。

これにメタデータを付与すると全天球動画と認識されるようになります。手順は [こちら](https://support.google.com/youtube/answer/6178631?hl=ja) 。

メタデータを付与したファイルを Windows 10 の "映画 & テレビ" アプリで再生すると全天球動画として再生されますが

![5_.png](https://qiita-image-store.s3.amazonaws.com/0/211064/260fe121-4486-102a-d6bc-1b133a9db1b8.png)

オブジェクトがありません。ドラッグして後ろを見てみると

![6_.png](https://qiita-image-store.s3.amazonaws.com/0/211064/2a919b59-86d9-f330-a5d1-e3b9cf17f3d0.png)

いました。ちなみに Game View では普通に見えていますし、 "Specific Camera(s)" (通常のカメラ) で録画した場合も普通に見えています。


## 原因

まず equirectangular 映像を生成する手続きですが、次のようになります。

1. Camera.RenderToCubemap でカメラ位置周囲の映像を cubemap にレンダリングする。
2. 1 でレンダリングした cubemap から RenderTexture.ConvertToEquirect で equirectangular 映像を RenderTexture に描画する。

となっていますが、このどちらにも "向きを指定するパラメーターがない (Camera.RenderToCubemap は Rotation も見ていない) " のです。なので

* cubemap レンダリング時はワールド座標系 Z 正方向にカメラを向けた時が正面 (0 度) になる。
* equirectangular レンダリング時は出力映像の中央 (0.5, 0.5) が 0 度になる。

となった結果なわけです。

![9_.png](https://qiita-image-store.s3.amazonaws.com/0/211064/c5f712fc-1fc7-1d8f-0b31-6d8d870cb5e3.png)

このような状態なのですが、カメラの向きに関わらず、ワールド空間の Z 正方向が正面としてレンダリングされます。

ということで Unity 2018.1b2 の組み込み機能をそのまま使うと非常に使い勝手の悪いやり方をしないと思ったような動画が作れなくなってしまっています。 (カメラを固定してシーンの方を動かすとか)

## 解決編

### 映像レンダリングの改善

次のようにします。

1. Camera.RenderToCubemap で cubemap を作成する。
2. 1. でレンダリングした cubemap から自前で equirectangular のレンダリングをする。このレンダリング処理の中で RenderTexture へのレンダリング位置や角度を調整する。

equirectangluar のレンダリングは下記の記事を参考にさせていただきました。ありがとうございます。

[Unityでカメラのequirectangular画像を作成してみる](https://qiita.com/mechamogera/items/0b47e5947f1eee2467da)

基本的にはこちらの記事の通りですが、このままだと RenderTexture.ConvertToEquirect と結果が同じになるので Shader のカスタマイズをします。

* [Converting a Cubemap into Equirectangular Panorama - Stack Overflow](https://stackoverflow.com/questions/34250742/converting-a-cubemap-into-equirectangular-panorama)
* https://github.com/Mapiarz/CubemapToEquirectangular/blob/master/Assets/Shaders/CubemapToEquirectangular.shader

こちらをカスタマイズし、

* レンダリング位置の指定
* Camera の向きに追従

をできるようにします。

#### シェーダー

https://github.com/aosoft/UnityVRVideoRecordingTest/blob/master/Assets/CubemapToEquirectangular.shader

* マルチシェーダーバリアントで出力先を Vertex Shader で指定できるようにしました。
* 回転行列を uniform で受け取るようにし、 Fragment Shader で Cubemap からのサンプリングする際の座標計算に適用するようにしました。

#### CSharp

Camera へのアタッチ専用としています。

https://github.com/aosoft/UnityVRVideoRecordingTest/blob/master/Assets/CubemapToEquirectangular.cs

* 内部で生成する Cubemap へ Camera.RenderToCubemap でレンダリング
* シェーダーの各種パラメーターへの反映。
    * public field の反映
    * Camera の rotation から行列を取得し、シェーダーへ反映
* Graphics.Blit で equirectangluer 映像をレンダリング。  

RenderInStereo = true の場合は上記を左右それぞれに行います。

## 使い方

### RenderTexture の用意

"Assets - Create - RenderTexture" でプロジェクト上に RenderTexture を作成しますが、 equirectangular の映像は **画面比が 2:1** になりますのでそれに準じた大きさに設定しておくのが無難でしょう。

左右別々の映像を用意するステレオ版ではこれを上下の縦に並べることで結果的に **画面比 1:1** の正方形としますので、 RenderTexture もそのように作成してください。

### スクリプトの適用と設定

1. CubemapToEquirectangular.cs と CubemapToEquirectangular.shader を適用するプロジェクトにコピーします。
2. CubemapToEquirectangular.cs を録画ソースとする Camera に適用します。
3. CubemapToEquirectangular に設定をしていきます。
    * RenderTarget - equirectangular のレンダリング先となる RenderTexture
    * Cubemap Size - cubemap のサイズ (幅高さ同サイズ)
    * Render In Stereo - チェックを入れるとステレオ映像としてレンダリングします。上半分が左目用、下半分が右目用。
    * Stereo Separation - ステレオレンダリング時の仮想的な両目間の距離 (IPD) を指定。通常は初期値のままで。

### 動画の作成

1. "Recorder" アセットをプロジェクトにインポートする。
2. メニュー "Tools - Recorder - Video" で Recorder アセットの設定パネルが表示し、下記のように設定する。
    * "Collection method" を "Render Texture Asset" にする。
    * 先に用意した RenderTexture を "Source" に指定する。
3. その他、お好みの設定を行う。
4. "Start Recording" で録画。

個人的には MP4 で出力するよう PNG で連番出力した後、別途 MP4 にエンコードした方がよいように思います。

### サンプルプロジェクトでの適用

サンプルプロジェクト (UnityVRVideoRecordingTest) はほぼ仕込み済になっていまして、簡単に試すことができるようにしてあります。

* RenderTexture として単眼用 (RenderTextureMono) と両眼用 (RenderTextureStereo) を用意してあります。
* "Main Camera" には単眼レンダリング用、両眼レンダリング用の設定済スクリプトを適用しています。

* 単眼 (モノラル) の場合
    * Recorder アセットの Source に "RenderTextureMono" を設定。
    * CubemapToEquirectangular の上側のインスタンスを有効化、下側を無効化。
* 両眼 (ステレオ) の場合
    * Recorder アセットの Source に "RenderTextureStereo" を設定。
    * CubemapToEquirectangular の下側のインスタンスを有効化、上側を無効化。

![16_.png](https://qiita-image-store.s3.amazonaws.com/0/211064/7f4cd234-f7d2-0dee-1167-2ff6ba8b55d2.png)

### 結果

![13.png](https://qiita-image-store.s3.amazonaws.com/0/211064/f18b8115-994f-4177-3d19-d49778161d72.png)
![14_.png](https://qiita-image-store.s3.amazonaws.com/0/211064/8cc71470-fe16-7839-c137-740878cc3b9f.png)

正面にオブジェクトが出るようになりました。

## おわりに

結果的にはたいしたことをしていない事になってしまいましたが、ここまでに至るのに結構苦労してしまいました。

equirectangular のレンダリングでの角度調整は簡単な事をしているだけなので、公式に実装されてもいいように思います (というかしてほしい) 。

実は一番はまってたのは今回記事にした向きの問題ではなく cubemap 作成の方なのですが、解消していません (記事でも触れていません) 。今回の記事の例ではあまり顕在化しないようにしています。こちらはできればまた改めて記事にしたいと思います。

RenderTexture をソースに Recorder アセットで録画、はかなり便利に思いました。今後もこのやり方でいろいろやってみようかと思います。