【Unity】Timelineとマルチシーンでライトベイク済みの背景モデルを切り替えるエディタ拡張『LightProbe Binder』を公開しました ＋ ライトマップ等の動的切り替えについて

# 概要
ゲームオブジェクトとライトプローブを紐付けるエディタ拡張機能を公開しました。
[Github LightProbeBinder](https://github.com/tsujihaneta/LightProbeBinder)
Unity 2020.3.29f1 (LTS)

# 最新情報（随時更新）
(2022/03/11 告知) ツールと記事のアップデートを行いました。

(2022/03/12 続報) なんと3日前に以下GitHubリポジトリで「Runtime Light Probes」という機能が公開されていました。
https://github.com/Ayfel/PrefabLightmapping
Timelineでの利用は調整が必要ですが、マルチシーンでなくプレハブで管理したい場合はこちらの利用を検討しても良さそうです。
※私の手元で試したところ、Ambientの設定のせいか求めている見た目にはなりませんでした。

(2022/03/13 関連情報)
Unity 2021.2で追加される「Sequences」機能により、ベイクされたLightingを切り替えられる可能性が出てきました。（そうじゃないと実用が難しいと思うので）
[Unity2021.2で映像制作 手順を確認しよう](https://www.youtube.com/watch?v=74VblliIzfQ)
こちらにより、この記事が不要になることを願います。
→（さらに追記）Realtimeだけでした（泣）。しかし、シーンのアクティブをまるごと切り替えられるTruckが追加されていましたので、シーン間参照問題をある程度解決できそうです！

# できるようになること
以下のような場合を想定しています。
- ミュージックビデオなどでよく見る、背景モデルが切り替わる演出
- 過去回想など、ロケーションがまるごと変わる場面転換
など

**Before（最後に追加されたシーンのライトプローブが使用されます）**
![Before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/051d8dfc-2637-2f2b-7831-0dd26eb3c641.gif)

**After（適切なライトプローブが使用されます）**
![After.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/473bc604-e4e7-c599-8d02-9da0164a6462.gif)

**使用例**
![ezgif-4-86a13dc318.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/2113a7b5-4f32-8676-9e50-c22a17a5687a.gif)
♪( 'ω' و(و"

ノリノリまさのり。
Timeline等で上記のようなものを作りたいケースは、結構あると思います。
つまり、リアルタイムで動くキャラクターなどがいて、背景をまるごと置き換えるというものです。

こんなにシンプルかつ誰でもやりそうなことが、Unityでは結構頭を使わないとできません。
私が知らないだけかも知れませんが、少なくとも自前で解決策を考えたほうが早いと思うくらいには情報が見つかりません。
もし「こうやればいいじゃん」という解決策をご存じの方は、ご連絡いただけると嬉しいです。（そのための記事でもあります…）

特に困るのはライティングの扱いです。
・ライトマップ
・ライトプローブ
・リフレクションプローブ
など、Unityにはライティングを構成するいくつかの要素があります。
それらをまるっと取り替えるのが、案外難しいです。

実際、私は数週間頭を悩ませました。
公式のTimelineサンプルであるThe Phantom KnowledgeとUltimate Bowl 2017を確認しましたが、ベイク系のライトは使っていないようでした。（嘘だろ……）
その問題を解決するために作成したのが『LightProbe Binder』になります。

# 使用方法
1. GitからLightProbe Binderをプロジェクトに追加します。（Examplesフォルダは削除しても構いません。）
2. 加算シーンとして使用する背景のみのシーン（以下「背景シーン」）を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/11057b87-3ea0-e8a1-d740-8615cf1e52a5.png)
3. staticで背景に使用するすべてのオブジェクトを一つのGameObjectの子オブジェクトにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/57d6e7f8-21af-c5c4-410c-1b5c89a431b2.png)
4. 「背景シーン」を単体で開いた状態で、通常通りLightingウィンドウの「Generate Lighting」でライトをベイクします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/2a3035c6-00a4-12da-ca31-7b16d88e940d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/37bb56f7-cac4-cabf-0e59-d3e934fab055.png)
5. 「Tools」→「Light Probe Binder」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/5718c0e5-67f3-17a3-900a-299c9c6bb7ec.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/987920c9-6a16-95d6-e70a-7376de4530da.png)
6. 「Binding Settings」の「Root Object」に2で作成した背景オブジェクトのRootオブジェクトを指定し「Subscribe」ボタンを押します。
![スクリーンショット 2022-03-11 185516.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/c09413db-0446-2187-efeb-fb2f3ad877fe.png)
7. LightProbesAssetが生成され、「Owner Management」に先程のオブジェクトが登録されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/dbb2e19a-ef59-7d82-ab7f-753e7eafef7a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/f87f056d-0ada-7625-b40b-ef098c5de656.png)
※Ownerになったオブジェクトには「LightProbeRestorer」コンポーネントがアタッチされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/f666910f-f5b8-3c02-3202-b56af4db08bf.png)
8. これで設定は完了です。以降、このオブジェクトが有効化された際自動でLightProbeが読み込まれるようになります。
![ezgif-1-2d3cda03ff.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/085c1037-6f6c-4d6b-34f7-06a0ae65e406.gif)


# その他機能紹介

**各種設定機能**
- 「Light Probe Binder」ウィンドウの「Project Settings」「Open Project Settings」ボタンからプロジェクト設定を開くことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/018ac0ca-ed25-32e5-c7f8-35987564e513.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/23ff84e4-1c45-220c-5ada-2193d50e9890.png)
- Auto Open Window
ライトのベイクが完了したら自動的に「Light Probe Binder」ウィンドウを開きます。
- Auto Update
ライトのベイクが完了したら自動的にOwnerに登録済みのLightProbeAssetを作り直して更新します。
- Disable Dialog
処理完了時の各種ダイアログを無効化します。

**Light Probe Binder ウィンドウのその他機能**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/471465e9-2286-1ab0-eb4c-5f7a44d99008.png)
- Status Monitor - Activate Light Probe
使用されているライトプローブを確認できます。
(Clone)はシーンのLightingData.assetが持っている、デフォルトのライトプローブです。
- Owner Management - MergeType プルダウン
「Overwrite」はデフォルトのモードです。「Additive」はシーンのライトプローブと連結する想定のモードですが、使用しないことをおすすめします。
- Owner Management - Activate Probe
本来、オーナーが所有するライトプローブはオーナーがアクティブになったとき適用されますが、「Activate Probe」を押せばシーンに即座に適用できます。
- Owner Management - Unsubscribe
参照しているLightProbeAssetファイルを削除した上で「LightProbeRestorer」コンポーネントをデタッチし、オーナーの登録を解除します。

# Timeline等でのシーン間参照について
ちなみにサンプルではゴリ押しでシーン間参照を解決していますが、
以下のような有料アセットを使う、自作するなどでもっとスマートに対応は可能かと思います。
[Advanced Multi-Scene: Cross-Scene References!](https://assetstore.unity.com/packages/tools/utilities/advanced-multi-scene-cross-scene-references-47200)

# スクリプトから切り替えたい
オーナーからLightProbeRestorerコンポーネントを参照し
`LightProbeRestorer.Merge()`で有効化
`LightProbeRestorer.Purge()`で無効化
できます。

# Unityの背景モデル切り替えについて
（ツールの説明については前の項目までで終わりです）

皆さんは以下のような場合、どうしているでしょうか。

例：
シーンAに
・ステージモデルA
・ステージモデルB
がある。
これを表示/非表示(アクティブの切り替え)で切り替えたい。

モデルだけなら本当に表示のON/OFFだけなので簡単です。
しかし当然、ライティングも切り替えたいです。

現在のUnityにおいて、ライトデータをベイクしないという選択肢はほとんどないかと思います。
そんなわけで、だいたい以下のような感じだろうと試します。（そして死ぬ）
1. Aだけを表示した状態(アクティブ)でベイク
2. Bだけを表示した状態(アクティブ)でベイク
→結果：Aのライトマップが上書きされて死ぬ

失敗しました。

当然といえば当然です。2のベイクをした際、1の情報は上書きされ天に召されました。

ちなみに1で生成されたライトデータをリネームし退避、その後2をやっても参照が死ぬので特に変わりません。
結果、以下のような残念な状態を右往左往して苦しめられることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/272076/6aca987f-1adc-a392-26a0-7680cc31f207.png)
｡ﾟ(ﾟ＾ω＾ﾟ)ﾟ｡

# Bakery使ったりライトマップをプレハブに格納するアプローチ
（書き終わって気づきましたが、この項目全部コラムです）
・ライトマップ
・ライトプローブ
・リフレクションプローブ
のうち
・ライトマップ
はプレハブに格納するという選択肢が存在します。
ライトマップに関しては、これで解決も可能です。

有志の方が作ってくださったありがたいソースも公開されています。
[PrefabLightmapping](https://github.com/Ayfel/PrefabLightmapping)

そして、
・リフレクションプローブ
は素直に使いたいシーンで焼けばOKです。

となると、残るは
・ライトプローブ
です。

[Bakery - GPU Lightmapper](https://assetstore.unity.com/packages/tools/level-design/bakery-gpu-lightmapper-122218?locale=ja-JP)
という有名Assetには、前述したライトマップのPrefab格納機能が標準で搭載されています。
そしてなんとライトプローブの代替となるBakeryVolumeなるものがあり、これもPrefabに格納できるとのこと！

もう解決じゃん！
……そうは問屋が卸しません。
このBakeryVolume、使用するには付属の専用シェーダーに置き換えるしかないのです。
そんなことしたら、UTS2で作った可愛いうちの子が台無しに…。
というかそもそも全マテリアルのシェーダーを置き換え、ないしは書き換えしなくてはいけないなんてとても現実的とは言えない気がします。
ちなみにライトもすべてBakery専用のものに置き換えるしかないので、導入するにはBakeryとともに心中する覚悟が必要かなと思っています。（それなりに実績のあるツールですが、労力がエグい）

かつてBakeryの一番大きな利点はGPUでのライトマップベイクが可能であることとされていましたが、現在はUnityにもGPU Progressive Lightmapperが搭載されています。
まだまだ現役ではありますが、LightProbeに関してはやはりUnity標準のものを使いたいところです。（個人的意見）

そして振り出しに戻る。

# それでも単一シーンで対応する場合
[Unityゲーム開発者ギルド](https://qiita.com/organizations/unity-game-dev-guild)というコミュニティにて、他の方はどのように対応しているのか質問したところ
以下のような方法が考えられるという旨の回答をいただきました。（感謝）

- ライトが干渉しない距離まで各モデルの座標を遠ざける
- ライトの影響するレイヤーを分ける

これらをやったあと、1回ベイクするとのことです。確かに出力ファイルや参照データが上書きで死ぬことはありません。
お手軽な気もしますし、これが有用なケースも当然有るかと思います。

ですがその反面、とても力技に感じる上、デメリットも大きく感じます。
- ステージBだけ変更したのに、ステージAも含めてベイクし直すことになる（時間がかかる）
- DirectionalLightなど距離制限のないライトが増えるたび専用のレイヤーを設ける必要がある
- アンビエントライト（環境光）などシーンが保有するライト情報で困る
- 単純に座標がバラバラで気持ちが悪い

今回切り替えを想定しているモデルは、完全に独立しておりお互いに鑑賞しないようなものですので
できれば各シーンはユニットに分け、ベイクも各々で済ませたいところです。

大前提として、以下の原則を抑えておくこととします。
- ライトベイクは1シーン1回が基本（＝単一シーンで複数背景を使いたいなら離して並べる）
- ライトプローブは焼き直さないと動かせない

一回しか焼けないという制約がある以上、やはり単一シーンで背景を切り替えるのはあまり現実的ではありません。
ランタイムでスクリプトによる切り替えを行うという方法もありますが、Timelineでは使えない上少し上級者向けです。
できるだけ変なことはしたくない……。
そこで思いつくのはマルチシーンによる対応です。

# 複数シーン（Multi-Scene Additive）で（『LightProbe Binder』を利用して）対応する場合
ようやくツールの中身の紹介に来ました。
今回のエディタ拡張はこちらのワークフローを想定し、その簡略化を図っています。

複数シーンの場合、まず以下のような感じにシーンを分けます。
・メインシーン(アクティブなシーン)
・ステージAシーン（Additiveで加算シーンとして追加で読み込む）
・ステージBシーン（Additiveで加算シーンとして追加で読み込む）

そして、
・ステージAシーンでライトベイク
・ステージBシーンでライトベイク
のように個別にライトをベイクします。
すると、これらを読み込んだとき
必要なライトマップは自動で読み込まれます。
この時点で、ライトマップに関しては勝利していると思います。
特に余計なことをする必要はありません。ないはず。

リフレクションプローブに関しても同様です。
事前に個別のシーンでベイクしておけば、利用シーンで特に何もせずとも反映されてくれます。

しかし、ライトプローブはざっくりいうと以下のような挙動になります。

- シーンをロードまたはアンロードすると、Unity は自動的に LightProbes オブジェクトを更新し、現在ロードされているすべてのシーンのすべてのライトプローブの位置と係数を加える（[ライトプローブとシーンのロード](https://docs.unity3d.com/ja/2019.4/Manual/light-probes-and-scene-loading.html)より参照）

加える、というのは「合体させて」「混ぜる」のかと思っていましたが、実際に使ってみるとどうやら
- 一番新しく読み込んだシーンのやつだけを使う

つまり
・メインシーン(アクティブなシーン)
・ステージAシーン（Additiveで加算シーンとして追加で読み込む）
・ステージBシーン（Additiveで加算シーンとして追加で読み込む）
の順に読み込むと、最後に読み込んだステージBシーンのものが使用されます。
そして仮にステージBシーンをアンロードしても、読み込んだライトプローブはシーンに残り続けます。

この仕様は、ある意味で正しいです。
Timeline以外の手法で背景を切り替えたいなら、
1. メインシーンを読む
2. ステージAシーンをAdd
3. ステージAシーンをRemove直後にステージBシーンをAdd
4. ステージBシーンをRemove直後にステージAシーンをAdd
（以下繰り返し…）

というようにすれば、適宜新しく読まれたシーンのLightProbeが使われ続けます。
（ちなみにシーンがLightProbeを持っていなければ上書きはされません）

LightProbes.TetrahedralizeAsync()等の関数を呼ばないとシーンに反映されないという記事も見かけましたが、私の環境では正しく切り替わっており詳細は不明です。
[LoadSceneMode.AddtiveでシーンをロードしてもLightProbeは更新されない](https://masakami.com/archives/2020/10/25/799/)
[LoadSceneMode.Additiveでシーンを追加した時にLightProbes(ライトプローブ)を反映する方法【Unity】](https://kan-kikuchi.hatenablog.com/entry/LightProbes_Tetrahedralize)

ですが、今回のようにTimelineを使ったり、素早く切り替わってほしい場合には
ステージAとステージBを両方Addしてある状態（読んでメモリに乗っている状態）で、シーン内のオブジェクトのアクティブを切り替えたいと思うはず。

んじゃあもう自主的に、Aがアクティブになったら「俺のを使え！」とシーンに渡し、使わなくなったら「俺のやつは捨てろ！」という感じにしたがいいのでは？と思い、今回のツールを作成しました。

以下サイトを参考にさせていただいています。（敬称略）
[テラシュールブログ 各シーンのライトマップは必要な時にロードされる](
https://tsubakit1.hateblo.jp/entry/2016/02/08/012822#%E5%90%84%E3%82%B7%E3%83%BC%E3%83%B3%E3%8%AE%E3%83%A9%E3%82%A4%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E3%81%AF%E5%BF%85%E8%A6%81%E3%81%AA%6%99%82%E3%81%AB%E3%83%AD%E3%83%BC%E3%83%89%E3%81%95%E3%82%8C%E3%82%8B)
[COREVALE コアベイル ただいまゲーム制作中 nity:ライトマップデータをゲーム実行中に切り替えてライティングを変える](http://corevale.com/unity/6307)

上記はどれも、ランタイムを想定した対処です。
しかし今回はできればTimelineのプレビューでも確認したい……。
ということで、[ExecuteAlways]実行を使い、Timelineでもちゃんと更新が反映されるようにしました。（重いかも）
このあたり、LightProbeAssetの切り離し出力から読み込みまでをできるようにしたのが『LightProbeBinder』になります。

中身はそれほど難しいものではありませんので、読めばわかるどころか苦情がたくさん来ると思います。
でも頑張ったので今回公開してみた次第です。

# 最後に
今回の問題に直面し、解決を試みるにあたり
先人の知恵にものすごくお世話になりました。
掲載しきれませんが数々の情報に助けられ、どうにかやりたいことができそうです。
また、SNS等で有益な情報を頂いた方々にも感謝申し上げます。
私もここに知り得た情報を残すことで、お礼と（備忘録に）かえさせていただきます。

お読みいただきありがとうございました。