皆さんこんにちは、コアテクグラフィクスチーム所属のチャンユービンです。
RenderGraph応用紹介記事の連載が終了してからしばらく経ちましたが、今回からはRenderGraph開発に関する小さなTipsを順次ご紹介していきます。
今回は、自作RenderPassの中からcameraColorを差し替えることで、戻しBlitを省く手法について紹介します。
サンプルプロジェクト
https://github.com/S20817/CoreTechBlogSample
SettingCameraColorSampleシーンをご覧ください
今後紹介するTipsのサンプルもそのリポジトリに入れていく予定です
あらすじ
ContextContainerから取得できるUniversalResourceDataのcameraColorを自作テクスチャに差し替える方法- Blit 1回削減の考え方と、使用できないケース
- うまくいかないケースでのフォールバック処理
前提と用語
- 実行環境:Unity 6 / URP 17+、RenderGraph有効
- RenderGraphの基本:パスは
RecordRenderGraphで入出力(依存)を宣言し、SetRenderFuncで実行 - UniversalResourceData:
activeColorTexture… 現在のアクティブなカラー。読み取り起点に使いやすいcameraColor… 以降のURPが参照する“カメラカラー”。get/set可能isActiveTargetBackBuffer… 直接バックバッファに描いているかの判定に使える- BackBufferは読み込み不可の仕様になっており、Blitのソースとしては使用できない
- TextureHandle:RenderGraphの仮想テクスチャハンドル
- renderGraph.CreateTexture(desc)で内部専用の一時テクチャが作成できる
RTHandleなど外部のリソースを使う場合はImportしてTextureHandle化できる
なぜ差し替えるとBlitを減らせるのか
典型的なポストエフェクトは次の2ステップになりがちです。
cameraColor→tempにBlit(エフェクト適用)temp→cameraColorに戻しBlit
2.の戻しをやめて、自作パスの最後でresourceData.cameraColor = temp;とすると、以降のURPパスはtempを“ActiveColorTexture”として参照します。結果、Blitは1回で済みます。
サンプルコード
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; public class TakeOverCameraColorPass : ScriptableRenderPass { private Material _material; // 全画面エフェクト用 public TakeOverCameraColorPass(RenderPassEvent evt) { renderPassEvent = evt; // 例:RenderPassEvent.AfterRenderingTransparents 等 } public override void RecordRenderGraph(RenderGraph rg, ContextContainer frameData) { if (_material == null) return; var res = frameData.Get<UniversalResourceData>(); var cam = frameData.Get<UniversalCameraData>(); // バックバッファ直書き中は読み戻しを避けます if (res.isActiveTargetBackBuffer) return; // 1) 入力(現在のカラー) TextureHandle src = res.activeColorTexture; // 2) srcと同仕様の一時テクスチャを確保 TextureDesc desc = rg.GetTextureDesc(src); desc.name = "_MyEffect_Temp"; desc.depthBufferBits = 0; TextureHandle temp = rg.CreateTexture(desc); // 3) src -> temp へBlit(エフェクト適用) using (var builder = rg.AddRasterRenderPass<PassData>("MyEffect_Blit", out var passData)) { passData.material = _material; passData.src = src; builder.UseTexture(src); // 読み取り builder.SetRenderAttachment(temp, 0); // 書き込み先 builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => { // 全画面描画(マテリアルの0番パスを使用) Blitter.BlitTexture(ctx.cmd, data.src, Vector4.one, 0, false, data.material, 0); }); } // 4) 戻しBlitの代わりに“以降のカメラカラー”を差し替え res.cameraColor = temp; } private class PassData { public Material material; public TextureHandle src; } }
この手法がうまくいかないケース
使いやすい手法ではありますが、使用制限があります
CameraStack有効時、以下の状況では使用できません(出力に反映されない)
- カメラにRenderPassを実行させている
- そのカメラのポストプロセスが無効
- そのカメラのポストプロセスが有効だが、AfterPostProcessing以降に実行させている
うまくいかない原因
とあるカメラでRenderGraphで作られたTempTextureは、次のカメラに引き継がれないので、cameraColorを差し替えても意味がありません。
ポストプロセスが有効な場合、UnityのUberPostPassがcameraColorをCameraColorTargetへBlitします。そのため、BeforePostProcessing以前に実行すれば問題はありません。
また、最後に実行されるカメラの場合は、最後にFinalBlitがcameraColorをバックバッファにBlitするので、それも問題ありません。
フォールバック処理
上記のケースに当てはまる場合は、cameraColorを差し替える代わりに、戻しBlitを実行するようにしましょう
サンプルコード
using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; public class TakeOverCameraColorPass : ScriptableRenderPass { private Material _material; // 全画面エフェクト用 public TakeOverCameraColorPass(RenderPassEvent evt) { renderPassEvent = evt; // 例:RenderPassEvent.AfterRenderingTransparents 等 } public override void RecordRenderGraph(RenderGraph rg, ContextContainer frameData) { if (_material == null) return; var res = frameData.Get<UniversalResourceData>(); var cam = frameData.Get<UniversalCameraData>(); // バックバッファ直書き中は読み戻しを避けます if (res.isActiveTargetBackBuffer) return; // 1) 入力(現在のカラー) TextureHandle src = res.activeColorTexture; // 2) srcと同仕様の一時テクスチャを確保 TextureDesc desc = rg.GetTextureDesc(src); desc.name = "_MyEffect_Temp"; desc.depthBufferBits = 0; TextureHandle temp = rg.CreateTexture(desc); // 3) src -> temp へBlit(エフェクト適用) using (var builder = rg.AddRasterRenderPass<PassData>("MyEffect_Blit", out var passData)) { passData.material = _material; passData.src = src; builder.UseTexture(src); // 読み取り builder.SetRenderAttachment(temp, 0); // 書き込み先 builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => { // 全画面描画(マテリアルの0番パスを使用) Blitter.BlitTexture(ctx.cmd, data.src, Vector4.one, 0, false, data.material, 0); }); } // 4) 条件チェック if (ここで使えるかどうかの条件チェック) { // 使える場合は差し替えで res.cameraColor = temp; } else { // 使えない場合は戻しBlit renderGraph.AddBlitPass(temp, res.activeColorTexture, _material); } } private class PassData { public Material material; public TextureHandle src; } }
まとめ
cameraColorを差し替えると、戻しBlitを回避できる- CameraStackを使用する場合は要注意です!
うまくいかない場合は、RenderGraphViewerやFrameDebuggerで、cameraColorの描画状況を確認しましょう