⚡
【URP14】残像が残るカメラを実装する
はじめに
URP14で、残像を作るようなカメラを作ってみました。
環境
Unity 2022.2.12
Universal RP 14.0.6
GitHub サンプル
標準カメラでは残像を作れない
URPの標準のカメラでは、 残像が表示されません。
残像が表示されない
残像を表示できない理由
これは、カメラのレンダリングが始まるタイミングでレンダーターゲットのクリアが行われてしまうためです。
直前フレームの描画結果が消されてしまうため、残像が表示されません。
FrameDebugger
残像を作る
今回の記事では、残像が残るようなカメラを実装します。
実装のアプローチ
以下のアプローチで実装します。
- 描画終了時、カメラのレンダーターゲットをRenderTextureへコピー
- 画面クリアが行われた直後のタイミングで、RenderTextureをカメラレンダーターゲットへコピーする
STEP1. RenderTextureを生成する
以下のコンポーネントを、シーン内の好きなオブジェクトにアタッチします。
CameraImage.cs
using UnityEngine;
using UnityEngine.Experimental.Rendering;
public static class RenderConfig
{
public static RenderTexture CameraTexture { get; set; }
}
public class CameraImage : MonoBehaviour
{
private RenderTexture _renderTexture; // カメラ内容を表示したいRenderTexture
private MaterialPropertyBlock _materialPropertyBlock; // マテリアルのパラメータ変更用のMaterialPropertyBlock
private MeshRenderer _meshRenderer; // RenderTextureを表示用のMeshRenderer
private void Start()
{
Setup();
}
private void OnDestroy()
{
Release();
}
/// <summary>
/// セットアップ処理
/// </summary>
private void Setup()
{
_renderTexture = new RenderTexture(Screen.width, Screen.height, 0, GraphicsFormat.R32G32B32A32_SFloat);
// ScriptableRenderPassから見える場所にRenderTextureを登録する
RenderConfig.CameraTexture = _renderTexture;
}
/// <summary>
/// 解放処理
/// </summary>
private void Release()
{
if (_renderTexture != null)
{
if (Application.isPlaying)
{
Destroy(_renderTexture);
}
else
{
DestroyImmediate(_renderTexture);
}
_renderTexture = null;
}
}
}
STEP2. カメラ描画内容のコピー (ScriptableRendererFeature)
以下は、カメラ描画ターゲットとテクスチャの間のコピーを行うレンダーパスになります。
これを用いて、残像の描画を行います。
using UnityEngine;
using UnityEngine.Rendering.Universal;
/// <summary>
/// テクスチャコピーを行うRendererFeature
/// </summary>
public class CopyTextureFeature : ScriptableRendererFeature
{
[SerializeField] private Settings settings = new Settings();
private CopyTexturePass _copyTexturePass;
public enum CopyMode
{
CameraToTexture,
TextureToCamera,
}
[System.Serializable]
private class Settings
{
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRendering;
public CopyMode copyMode = CopyMode.CameraToTexture;
}
/// <inheritdoc/>
public override void Create()
{
_copyTexturePass = new CopyTexturePass
{
renderPassEvent = settings.renderPassEvent
};
}
/// <inheritdoc/>
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
_copyTexturePass.Setup(settings.copyMode);
renderer.EnqueuePass(_copyTexturePass);
}
}
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
/// <summary>
/// テクスチャコピーを行うRenderPass
/// </summary>
public class CopyTexturePass : ScriptableRenderPass
{
private CopyTextureFeature.CopyMode _copyMode;
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
base.Configure(cmd, cameraTextureDescriptor);
ConfigureClear(ClearFlag.None, Color.clear);
}
// Here you can implement the rendering logic.
// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 再生中のみ実行
if (!Application.isPlaying)
{
return;
}
if (RenderConfig.CameraPreviewTexture == null)
{
return;
}
// Gameカメラのみ実行
var camera = renderingData.cameraData.camera;
bool isGameCamera = (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR);
if (!isGameCamera)
{
return;
}
// テクスチャをコピー
var cmd = CommandBufferPool.Get("Copy Texture");
var cameraColorTargetHandle = renderingData.cameraData.renderer.cameraColorTargetHandle;
switch (_copyMode)
{
case CopyTextureFeature.CopyMode.CameraToTexture:
cmd.Blit(cameraColorTargetHandle, RenderConfig.CameraPreviewTexture);
break;
case CopyTextureFeature.CopyMode.TextureToCamera:
cmd.Blit(RenderConfig.CameraPreviewTexture, cameraColorTargetHandle);
break;
default:
throw new ArgumentOutOfRangeException();
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
public void Setup(CopyTextureFeature.CopyMode copyMode)
{
_copyMode = copyMode;
}
}
上記のRendererFeatureは、UniversalRenderPipelineAssetへ以下のように登録して使用します。
結果
以下のように残像が表示されます。
Discussion