- 概要
- 追記
- 略語
- 動作環境
- Tips Unity 2020
- 雑感
概要
UnityのUniversal Render Pipelineには Render Feature という描画処理を差し込む機能があります。
この Render Feature ですが、XRのSingle Pass Instanced 対応をする場合はクセがあります。
今回はそんなSingle Pass Instanced向けのRender FeatureのTipsを共有します。
追記
- 2022/03/27 「おまけ、IntermediateTextureMode.Alwaysの指定なしでも機能させる方法」項目を追加
略語
用語が長いので、以下では略語で紹介します。
| 用語 | 略語 |
|---|---|
| Universal Render Pipeline | URP |
| Render Feature | RF |
| Single Pass Instanced | SPI |
| Render Texture | RT |
動作環境
- Unity 2020.3.30f1 + Universal RP 10.8.1
- Unity 2021.2.16f1 + Universal RP 12.1.6
Tips Unity 2020
ポストエフェクトでは cmd.Blit は使用不可で cmd.DrawMesh を使用
詳細は以下のドキュメントに記載されています。
※以下、上記ドキュメントを 「SPI Blit Example」と呼びます
NOTE: Do not use the cmd.Blit method in URP XR projects because that method has compatibility issues with the URP XR integration.
互換性の問題でSPIでは cmd.Blit は正しく機能しないようで、
もし、ポストエフェクト的なことを実施したい場合は cmd.DrawMesh + _CameraColorTarget から画面キャプチャを取得して Blit を実現します。
サンプルのColorBlitRenderFeature について
ちなみに SPI Blit Example の ColorBlitRenderFeature のサンプルは描画の緑成分を抽出するポストエフェクトになっています。
↓

(2021.2以降) SwapBufferはSPIでは実質的に使用不可
Unity 2021.2 ではRFでポストエフェクトを実装しやすいようにSwapBufferという機能が追加されています。
残念ながらSwapBufferはSPIでは使用できません。。。(もし使用できる場合は教えていただけるとありがたいです!!)。
原因はSwapBufferの処理が隠蔽されていること、SwapBufferを使用するBlitメソッドが cmd.Blit を使用するためです。
※SwapBufferについては以下の動画をご参照ください。
画面キャプチャは不透明オブジェクトのみ
さて、ポストエフェクトと言いましたが、URP + SPIにおいては欠点があります。
不透明オブジェクトの描画結果しか取得できません
これは、シェーダーに渡されるテクスチャの名前 (_CameraOpaqueTexture) の通り、
不透明オブジェクトのみ描画した画面キャプチャが渡されるためです。
↓


SwapBufferも使用できないため、残念ながらSPIでのポストエフェクトは透過オブジェクトを対象にできないなど制限が強いです。。。
完全な描画結果が取れない理由
SPIにおいて、スクリーン向けRTにアクセスする術がないためです。
ScriptableRenderer.GetCameraColorFrontBufferが internalメソッドのためアクセスできない- (2021.2以降)SwapBuffer用のRTにアクセスできない
Unity 2021.2以降でのポストエフェクトにはIntermediateTextureMode.Alwaysの指定が必要
Unity2021.2から IntermediateTextureMode という設定項目が追加されました。
IntermediateTextureMode.Alwaysを指定しないとSPI Blit Exampleの ColorBlitRenderFeature は機能しません!
名前の通り、中間テクスチャ(IntermediateTexture)を使用するかの指定になります。

IntermediateTextureMode.Alwaysの指定が必要な理由
指定していない場合は cmd.DrawMesh の描画先が中間テクスチャではなくスクリーンのRTに直接描画されるのですが、
その後、FinalBlitPassによってスクリーンのRTが上書きされてしまうためです。
以下のコードにIntermediateTextureModeによる条件分岐があります。
github.com
SPI Blit Example には、注意書きがないのでIntermediateTextureMode.Alwaysの指定を忘れると Unity 2021.2 では実質的に描画結果に現れません。
判明するまで数時間かかりました。。。一応Report投げときました。
ちなみに非SPI(Multi Pass) や NonXRではSwapBufferが使用可能なので問題になりません。
Unity 2020.3 以前での中間テクスチャ
Unity 2020.3 + Universal RP 10 では IntermediateTextureMode は有りませんが、
実は RF が Forward Rendererに1つでも追加されている場合に、中間テクスチャが自動で有効になっていたようです。
以下のコードの rendererFeatures.Count != 0 という判定からわかります。
おまけ、IntermediateTextureMode.Alwaysの指定なしでも機能させる方法
※IntermediateTextureMode.Alwaysを指定する方が確実と思われますが、一応紹介します。
SPI Blit ExampleのColorBlitPassを以下のようにrenderingData.cameraData.renderer.cameraColorTargetを指定することで、
IntermediateTextureMode.Alwaysを指定していない場合でも機能させることができます。
--- a/Assets/ColorBlitPass.cs +++ b/Assets/ColorBlitPass.cs @@ -34,7 +34,7 @@ internal class ColorBlitPass : ScriptableRenderPass using (new ProfilingScope(cmd, m_ProfilingSampler)) { m_Material.SetFloat("_Intensity", m_Intensity); - cmd.SetRenderTarget(new RenderTargetIdentifier(m_CameraColorTarget, 0, CubemapFace.Unknown, -1)); + cmd.SetRenderTarget(new RenderTargetIdentifier(renderingData.cameraData.renderer.cameraColorTarget, 0, CubemapFace.Unknown, -1)); //The RenderingUtils.fullscreenMesh argument specifies that the mesh to draw is a quad. cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_Material); }
IntermediateTextureMode.Alwaysの指定なしでも機能する理由
renderingData.cameraData.renderer.cameraColorTargetは基本的に中間テクスチャとなっているため- 以下のコードを読む限り
createColorTextureがtrueの場合に中間テクスチャが描画先にになるのですが、スクリーン(非SceneView)向け描画は基本的にtrueになるみたいです
- 以下のコードを読む限り
createColorTexture == falseになる(中間テクスチャを描画先にしない)パターンのほうが珍しいですが、
SPIの場合で中間テクスチャを描画先にする場合は、renderingData.cameraData.renderer.cameraColorTargetを使用するならIntermediateTextureMode.Alwaysを指定する方が確実だと思います。
URP + SPI で可能な表現の例
不透明オブジェクトの描画結果を使用したポストエフェクト
透明オブジェクトは対象にできませんが、不透明オブジェクトならポストエフェクトをかけることができます。

アルファブレンドによるフェードイン・フェードアウト
シェーダーのアルファブレンドは問題なく使用できるため、フェードイン・フェードアウトは実現できます。
Blend SrcAlpha OneMinusSrcAlpha
...
half4 frag (Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
return half4(0, 0, 0, saturate(_Intensity));
}
Single Pass Instanced向けURP RenderFeatureでアルファブレンドが機能することの確認 pic.twitter.com/RH3MDPJwq2
— すぎしー (@tsgcpp) 2022年3月22日
雑感
なんとか3月中に1つ記事が書けました! 本当はAddressablesに関する記事を書いていたんですが、検証の自動テストがGameCIで実現できず。。。
Unity2020でRFを使用すると中間テクスチャが必ず使用されるようになるのも知るきっかけになってよかったです(RF使用する場合のパフォーマンスの懸念自体は増えましたが)。
世間は卒業シーズン、新卒入社で新生活を始める人も多そうですね。 これからUnity、XR始める人にも興味を持っていただければ幸いです!
それでは~