はじめに
カメラスタックでfovが違うカメラを重ねている状況でズームしたい場合
単にカメラ移動で近づけるといったアプローチでは、同じ移動量に対する拡大率がカメラごとに変わってくるので、うまくズームができません
(カメラごとに移動量を補正すれば可能だが…面倒でしょう)

(カメラ1 赤キューブ Fov60、カメラ2 青キューブ Fov30)
また、ズーム中心が常に画面中央であれば、各カメラのFovに対して同じ比率を掛ける方法でも上手くいったりしそうですが
そうでない場合は、ちょっと面倒くさそうです。
この辺をいい感じにできないかなと考えていました。
投影行列を触ってしまえ
UnityではCameraコンポーネントから投影行列を取得して直接操作できるので
これをいじってしまうアプローチです
CameraZoom.cs
using UnityEngine;
public class CameraZoom : MonoBehaviour
{
// BaseカメラとOverlayカメラを登録
[SerializeField] private Camera[] _cameras = new Camera[0];
[SerializeField] private float _zoom = 1.0f;
void LateUpdate()
{
foreach (Camera cam in _cameras)
{
// 行列をリセット
cam.ResetProjectionMatrix();
Matrix4x4 proj = cam.projectionMatrix;
// スケーリング XYのみ
proj[0, 0] *= _zoom;
proj[1, 1] *= _zoom;
cam.projectionMatrix = proj;
}
}
}
各カメラの元の投影行列に対して、同じだけXY方向にスケーリングすることでズームしています
ズームの中心を変える
ズームの中心を変えたい場合も、同様に行列を弄れば良さそうです。
CameraZoom.cs
using UnityEngine;
public class CameraZoom : MonoBehaviour
{
// BaseカメラとOverlayカメラを登録
[SerializeField] private Camera[] _cameras = new Camera[0];
[SerializeField] private float _zoom = 1.0f;
// ズーム中心
[SerializeField, Range(-1,1f)] private float _centerX = 0;
[SerializeField, Range(-1,1f)] private float _centerY = 0;
void LateUpdate()
{
foreach (Camera cam in _cameras)
{
// 行列をリセット
cam.ResetProjectionMatrix();
Matrix4x4 proj = cam.projectionMatrix;
// スケーリング XYのみ
proj[0, 0] *= _zoom;
proj[1, 1] *= _zoom;
// ズーム中心の移動
proj[0, 2] += _centerX * (_zoom - 1f);
proj[1, 2] += _centerY * (_zoom - 1f);
cam.projectionMatrix = proj;
}
}
}
まとめ
- Fovが違う複数のカメラを重ねていても、投影行列を直接弄ることで共通のパラメータで一緒にズームすることができた
小ネタにはなりましたが、ゲーム開発だとズーム演出を使う機会も多いと思うので
何かの役に立てば幸いです。

