はじめに
現在の描画に利用しているカメラを取得する際に Camera.current
というプロパティから取得できるはずが、取得しても以下の様にnull
になってしまっていたので原因を調べてみました。
void Start()
{
var currentCamera = Camera.current;
Debug.Log(currentCamera);
}
Built-in Render Pipelineの場合の取得方法
公式ドキュメントの記載の通り、OnRenderImage、OnPreRender、OnPostRender
以外では取得できないようです。
この関数は、 MonoBehaviour.OnRenderImage、MonoBehaviour.OnPreRender、MonoBehaviour.OnPostRenderのイベントのいずれかを実装する場合にのみ使用します 。
BRPの場合は公式に従って以下の様にそれぞれのイベント
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
Debug.Log($"OnRenderImage : {Camera.current}");
}
private void OnPreRender()
{
Debug.Log($"OnPreRender : {Camera.current}");
}
private void OnPostRender()
{
Debug.Log($"OnPostRender : {Camera.current}");
}
また、これらのイベントはCameraコンポーネントと同一のゲームオブジェクトにアタッチする必要があるので現在のレンダリング中のカメラを取得する用途には制約が多い印象です。
そこで、OnRenderObject
イベントで取得することでCameraをアタッチしているGameObject外でもCamera.currentを取得できるようです。
private void OnRenderObject()
{
Debug.Log($"OnRenderObject : {Camera.current}");
}
URPの場合の取得方法
URPではOnRenderImage、OnPreRender、OnPostRender
に加えてOnPostRender
これら全てが機能しないため、公式ドキュメントに記載されている方法ではCamera.currentを取得することができません。
そこで描画タイミングで発生するイベントRenderPipelineManager.beginCameraRendering
を購読することで最新の描画に利用したカメラを取得する方法でCamera.currentっぽい値を取得してみました。
Scene構成は以下の形で、MainCameraがpriorityを0、SubCameraがpriorityを-1に設定してMainCameraの優先度の方が高くなるように設定しています。
public class CameraCheck : MonoBehaviour
{
// 直近で描画に利用したカメラ
Camera _currentCamera;
private void Start()
{
// カメラ描画イベントを購読する
RenderPipelineManager.beginCameraRendering += WriteLogMessage;
}
private void OnDisable()
{
// カメラ描画イベントを解除する
RenderPipelineManager.beginCameraRendering -= WriteLogMessage;
}
void WriteLogMessage(ScriptableRenderContext context, Camera camera)
{
Debug.Log($"Beginning rendering the camera: {camera.name}");
// 最新の描画用カメラとして登録する
_currentCamera = camera;
}
private void Update()
{
// 最後に描画したカメラを表示する
Debug.Log("_currentCamera : " + _currentCamera);
}
}
この様にすることで以下のログのように優先度を高く設定したMainCamera
が最新のカメラとして取得できるようになりました。
本記事について、もっと簡単に実現できる方法があればコメントを頂けると幸いです。
余談
ちなみに最後のActionを使う方法ですがUniRx
やR3
で実装するとこの様になります。
取得したいフレームだけイベント購読する。みたいな方法で使えば便利かも知れません。
private void Start()
{
Observable.FromEvent<Action<ScriptableRenderContext, Camera>, Tuple<ScriptableRenderContext, Camera>>
(
h => (x, y) => h(Tuple.Create(x, y)),
handler => RenderPipelineManager.beginCameraRendering += handler,
handler => RenderPipelineManager.beginCameraRendering -= handler
).Subscribe(arg => WriteLogMessage(arg.Item1, arg.Item2));
}
ActionをStreamに変換することができるObservable.FromEvent
ですが、今回の様に引数が複数になるとタプルに変換して戻すような処理が入り一気に複雑な記述になってしまうため、可読性が悪いためActionのまま実装した方が良い気がしました。。。