【Unity】HDRPのCinemachineで同時に複数のカメラを使う際の注意点

# はじめに
HDRPのCinemachineで同時に複数のカメラを使うとき発生した問題についてまとめました。
今回扱う問題は以下の2つです。

* カメラごとに別々のVirtualCameraを割り当てることができない
* DoFがどちらか片方のカメラに上書きされてしまう

取り上げている例はTimelineを使わないものですが、Volume周りの話はTimelineを使う場合も同様です。
**対応方法を先に書きましたが、時間のある方は[予備知識](#予備知識)も読んでみてください。**

<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/fba35176-b3bd-838b-9eb9-e039d372e20d.png">

# 注意事項
HDRP+CinemachineとBuild-in+PPSの対応方法は異なります。
この記事は「僕はこの方法で対応しているよ」という紹介です。もっと良い方法を知っている人が居ましたら教えてください。

# ターゲット
* HDRPを触ったことがある
* Cinemachineを触ったことがある
* HDRPのCinemachineで同時に複数のカメラを描画したい
* 同時に描画するカメラのDoFを分けたい

# 検証環境
以下の環境で検証を行いました。

|項目|バージョン等|
|-|-|
|OS|Windows 10|
|Unity|Unity 2021.3.1f1|
|Cinemachine|2.8.6|
|プロジェクト|[GitHub](https://github.com/mkc1370/HDRPCinemachineTest)|

# 用語について
以下のような略称を使用します。
|正式名称|略称|
|-|-|
|CinemachineBrain|Brain|
|CinemachineVirtualCamera|VirtualCamera|
|CinemachineVolumeSettings|VolumeSettings|
|Volume Profile Asset|Volume Profile|

それぞれの用語とその概要です。
|用語|概要|
|-|-|
|Brain|VirtualCameraの設定をCameraに反映させるコンポーネント|
|VirtualCamera|TransformやFOVなどの設定を持つコンポーネント|
|Volume Profile|BloomやDoFなどのグラフィクの設定を持つアセット|
|Volume|Volume Profileを映像に反映させるコンポーネント|
|VolumeSettings|VirtualCameraが有効なときに使用するVolume Profileを指定するコンポーネント|

# 複数のカメラに別々のDoFを設定する例
複数のカメラを同時に描画し、DoFを別々に設定したい場合を例に説明します。

## 手順
### 1. レイヤーの作成
VirtualCameraやVolumeの設定を分けるために`(同時に描画したいカメラの数)+1`個のレイヤーを作成します。
今回は以下のように設定しました。（レイヤー名は自由に変えても大丈夫です）

|レイヤー番号|レイヤー名|役割|
|-|-|-|
|10|Main|1個目のカメラ用|
|11|Sub|2個目のカメラ用|
|12|Common|全てのカメラに対して反映させたいVolume用|

ここで1点注意点があります。Commonレイヤーに相当するレイヤーは必ず全てのカメラのレイヤーの後に設定してください。上からCommon→Main→SubのようにするのはNGです。

### 2. Default Volume Profile Assetの設定
この方法ではScene ViewにはDefault Volume Profile Assetは今まで通り反映されますが、Game Viewには反映されなくなります。
そのため、以下のようにして対応します。

1. Sceneに新しいGlobal Volumeを追加する
2. VolumeのGameObjectのLayerをCommonに設定する
3. VolumeのProfileにDefaultSettingVolumeProfileを設定する

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/b6baf1ab-d4ed-be66-bf4e-2a6cf2b97978.png">

### 3. Cameraの設定

#### 1. 2個のカメラを作成する
それぞれの名前はMainCameraとSubCameraとしました。
ここの名前は自由に変えても大丈夫です。

#### 2. Cameraのレイヤーを変更する
* MainCameraのレイヤーをMainにする
* SubCameraのレイヤーをSubにする
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/8ae75294-955f-cce9-a5d4-23873db53e2b.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/a758f8fa-ff4b-cbe4-4b1e-176cbf113d2f.png">

#### 3. 両方のCameraにCinemachineBrainをアタッチする


#### 4. CameraのCulling Maskを設定する
* MainCameraからは**Sub**を除く
* SubCameraからは**Main**を除く

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/ae7ff7ab-e581-3318-7037-08047fafe450.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/c1b4fe81-fd8e-5d82-17d8-d0006ea5becc.png">

#### 5. CameraのVolume Maskを設定する
* MainCameraにはMain, Commonを設定する
* SubCameraにはSub, Commonを設定する

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/ab9b7fb6-8ea4-cd8c-da23-4450905480f7.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/5402f419-3ee5-fa7c-8832-1cae6a7c794b.png">

#### 6. SubCameraのTarget DisplayをDisplay 2にする
これは必須ではありませんが、わかりやすくするためにDisplay 2にしています。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/b2a27429-5ef3-47e0-ace1-44a134726de5.png">

### 4. VirtualCameraの作成
#### 1. 2個のVirtualCameraを作成する
それぞれの名前はVCMainとVCSubとしました。

#### 2. VirtualCameraのレイヤーを変更する
* VCMainのレイヤーをMainにする
* VCSubのレイヤーをSubにする

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/bc23e41c-6486-493e-5294-f5ed109b4cf9.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/1944db0c-cb1a-efff-cb67-e7f49aa68beb.png">

#### 3. 両方のVirtualCameraにCinemachineVolumeSettingsを追加する
VirtualCameraのExtensionsのAdd ExtensionのCinemachineVolumeSettingsから追加できます。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/f82959c7-830d-f2ce-b8a7-dff4089eec4b.png">

#### 4. 新しいVolume Profileを作成する
VCMainのVolumeSettingsのProfileの横のNewを押すと新しいProfileが作成されます。
ここで作成されたProfileをVCSubにも設定してください。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/2bbdfe59-670c-35eb-c8d2-fd057aec6bb5.png">

#### 5. Volume Profileを設定する
Volume Profileを画像のように設定してください。
ここで重要なことは以下の4点です。

* Depth Of Fieldにチェックが入っている
* Focus Modeにチェックが入っている
* Focus ModeがPhysical Cameraになっている
* Focus Distanceにチェックが入っている

Focus Distanceの数値はVolumeによって変更されるため、何が入っていても大丈夫です。必要に応じてQualityなどを設定してください。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/1042a705-4c15-44a4-78e1-7763bfbc84ab.png">

#### 6. ピントを合わせる対象を設定する
両方のVirtualCameraのVolumeSettingsのFocus TrackingをCustom Targetに変更し、それぞれ別のFocus Targetを設定してください。
ここでは、Custom Targetにしていますが、他のものに変更しても大丈夫です。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/d4125ad4-e83b-0c08-d032-b3e921d46393.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/919cc22a-f9b6-dced-a14e-c6a42c10f297.png">

## 結果
GameViewを2個用意し、それぞれDisplay1とDisplay2を表示するとこのようにそれぞれ表示すると1枚目の画像のようになり、それぞれ別の場所にピントが合っていることが分かります。
しかし、上記のようなレイヤー分けを行わないと2枚目のように両方とも同じ位置にピントが合ってしまいます。これはVolumeのDoFの設定が競合してしまうためです。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/32d84742-198f-ab57-0d99-07347735f57e.png">
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/91b113ae-2e90-7b92-2b07-939bdeb0a29f.png">


# 予備知識
この記事の内容を理解するために必要な知識について説明します。
知っている項目は読み飛ばしても大丈夫です。

## VirtualCameraの選ばれ方について
Volumeが共通でも問題ない場合はこれだけ理解していれば大丈夫です。
タイムラインを使用しない場合はBlainがあるCameraのCulling Maskに依存します。
ここでチェックが入っているレイヤー（この画像ではDefault, Main, Common）にあるVirtualCameraの中で最もPriorityが高いものが選ばれます。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/d2c300e4-9dfb-9439-18a9-e1d5d1528857.png">
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/f5672cdd-c799-d81f-012c-2f35170a5a74.png">

おすすめの記事
* [【Unity】複数のCinemachine Brainとカメラを扱う方法 | ねこじゃらシティ](https://nekojara.city/unity-cinemachine-multi)
グラフィカルで分かりやすいです。
* [【Unity】Cinemachineで複数のカメラを使用する - テラシュールブログ](https://tsubakit1.hateblo.jp/entry/2018/09/24/083000)
タイムラインでの説明も書かれています。

## Volumeについて
これはカメラ毎にVolumeを分ける場合に理解する必要があります。
主にカメラ毎にDoFを設定したい場合に有効です。

### Volumeの概要
HDRPにはHDRP Volume Frameworkという、グラフィックの設定を変更する仕組みがあります。
これはBuild-inのPPS(Post Processing Stack)に似ていますが、一般にそれよりもできることが多いです。
グラフィックの設定はVolume Profileに保存して以下のような方法で使用します。
|方法|有効範囲|割り当てられるレイヤー|
|-|-|-|
|Default Volume Profile Asset|プロジェクト全体|Default|
|Volumeコンポーネント|Scene全体・一部|GameObjectのLayerと同じ|
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/d36fe689-2da9-88cf-2b1b-6a4d1de540a9.png">
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/5602ae59-93b3-962b-cae6-84747920469c.png">

おすすめの記事
* [ポストプロセスだけじゃない！HDRP Volume Framework | Unity Learning Materials](https://learning.unity3d.jp/6852/)
HDRP Volume Frameworkの基礎が分かります。
* [Bug - Default Volume Profile only work with default Layer. - Unity Forum](https://forum.unity.com/threads/default-volume-profile-only-work-with-default-layer.1095721/)
Default Volume Profile AssetがDefaultのレイヤーに割り当てられることが書かれています。

### Volumeの反映方法
前述の通り、Volumeはいずれかのレイヤーに割り当てられます。
反映されるVolumeはCameraのVolume Mask[^1]に依存します。
ここでチェックが入っているレイヤー（この画像ではMain, Common）にあるVolumeが反映されます。
**Defaultのチェックを外した場合はDefault Volume Profile Assetで設定したProfileが反映されません。**
[^1]: 厳密にはCameraではなくHD Additional Camera DataにHDRP用の設定が保存されています。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/2247276a-bba2-887f-22bb-f00df8d8db30.png">

### VolumeSettingsに設定されたVolume Profileについて
VirtualCameraに対してVolume Profileを適用したい場合はVolumeSettingsにVolume Profileを設定します。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/1d12e336-a2f6-b812-cda2-bd85d23d9742.png">
Volume Profileが設定されたVolumeは次のように作成されます。

1. VolumeSettingsは自身のVirtualCameraが有効になった際にVolume Profileに設定されているVolumeの実体を作成する
2. 1で作成されたVolumeはCameraの**VolumeMask[^1]内で有効化されている一番上のレイヤー**と同じレイヤーに設定される

ここで重要なのは、生成されるVolumeのレイヤーはCameraのVolumeMaskに依存することです。この写真の場合は**VolumeMaskで有効化されているレイヤーの中で一番上**であるMainに設定されます。
また、このVolumeはBrainが付いているGameObjectの子オブジェクトとして生成されますが、Hierarchyなどには表示されない設定になっています。

<details><summary>補足情報</summary><div>

`CinemachineVolumeSettings.cs`についてです。
（Unity Companion Licenseの再配布関係がよく分からなかったので、ソースコードは記載しません）
まず、Hierarchyなどに表示されないようにする設定は306行目で行われています。
また、VolumeMaskの一番上をVolumeのLayerに設定していることが分かるのは318行目から326行目の部分です。

</div></details>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320192/5536a4bd-54ab-d1bc-d47c-71f65cbac0f7.png)

# まとめ
今回はHDRPで複数のカメラを同時に描画して、それぞれ別々のDoFを適用するというニッチ（？）な内容の記事になりました。僕が調べた時点ではこの問題の対応方法が見つからず結構苦戦していたので、誰かの役に立てたら嬉しいです。