Notifications
Article
Lightweight Render Pipeline(LWRP) のパイプラインを拡張する
Updated 5 months ago
1.8 K
0
Lightweight Render Pipeline(LWRP) のプレビューがはずれ,今後本格的に利用していくためにパイプラインの拡張方法について調査しました

はじめに

Unity2019.1f2 のリリースに伴って Lightweight Render Pipeline(LWRP) もついに Preview がはずれました。LWRP はモバイル‥VR も視野に入れた,軽量かつモダンな Unity の新しい描画パイプラインです。これからは積極的に使っていきたいところですが,今までのパイプラインと互換性のない部分も多くあり,過去のパイプラインから移行ができるか不安な面もあります。LWRP で動作しなくなるものの一つに,カメラコンポーネントにセットして機能するイメージエフェクト類が挙げられます。今回はこれらのカメラエフェクトを LWRP way で実装 or 移行する方法を調べてみます。

描画パイプラインを拡張するための機能

今までは Camera コンポーネントに対して,AddCommandBuffer を呼ぶことで,任意のタイミングに描画処理を挟むことができましたが,LWRP ではこのやり方はサポートされません。CommandBuffer を LWRP のパイプラインに登録してあげる必要がありますがどうすればよいのでしょうか。

ScriptableRendererFeature

今回,正式リリースに伴って ScriptableRendererFeature という 独自の描画パスを差し込んで LWRP のパイプラインを拡張する機能が使えるようになりました(CHANGELOG)。ScriptableRendererFeature は ScriptableObject として提供され,LWRP の Forward Renderer を自由にプラグイン形式で拡張するためのオブジェクトのようです。

ScriptableRenderPass

ScriptableRendererFeature では,AddRenderPasses コールバック内で LWRP の Renderer を触れるようになっており,ここで独自の ScriptableRenderPass を登録して描画パスをパイプラインに差し込んでいきます。ScriptableRenderPass は LWRP のパイプライン中における個々の描画パスを扱います。"不透明オブジェクトの描画","Skybox","ポストプロセス"といった描画パスも LWRP 内では ScriptableRenderPass によって個々に実装されています。独自の描画パスはこの ScriptableRenderPass を継承して実装すれば良さそうです。ScriptableRenderPass はパイプラインに登録されると,描画処理のために Execute コールバックが呼ばれるので,ここに実際の CommandBuffer 組み立て処理を記述すると良さそうです。

ScriptableRenderer

ScriptableRendererFeature の AddRenderPasses コールバックには,LWRP のレンダラーとなる ScriptableRenderer が渡されます。このレンダラーに対して ScriptableRenderPass を登録します。

描画タイミング

描画タイミングは ScriptableRenderPass 内に定数として定義されています。今までカメラエフェクトで指定していたタイミング(CameraEvent)と同等のことが問題なくできそうです。
public enum RenderPassEvent { BeforeRendering = 0, BeforeRenderingShadows = 50, AfterRenderingShadows = 100, BeforeRenderingPrepasses = 150, AfterRenderingPrePasses = 200, BeforeRenderingOpaques = 250, AfterRenderingOpaques = 300, BeforeRenderingSkybox = 350, AfterRenderingSkybox = 400, BeforeRenderingTransparents = 450, AfterRenderingTransparents = 500, BeforeRenderingPostProcessing = 550, AfterRenderingPostProcessing = 600, AfterRendering = 1000, }

大まかな流れ

これで登場人物は揃いました。 ScriptableRendererFeature と ScriptableRenderPass をそれぞれ継承することで独自の描画パスを作ることができそうです。 一度流れを整理します。
  1. ScriptableRendererFeature を継承して MyScriptableRendererFeature (ScriptableObject) を作る
  2. ScriptableRenderPass を継承して MyScriptableRenderPass を作る
  3. MyScriptableRendererFeature の AddRenderPasses 内で, MyScriptableRenderPass を LWRP の ScriptableRenderer に登録する
では,サンプル実装をしてみます。

コードを書く

SampleLWRPScriptableRendererFeature .cs

ここでは SampleLWRPScriptableRenderPass を生成して ScriptableRenderer に登録します。
using UnityEngine; using UnityEngine.Rendering.LWRP; [CreateAssetMenu(fileName = "SampleLWRPScriptableRendererFeature", menuName = "Sample/ScriptableRendererFeature", order = 1)] public sealed class SampleLWRPScriptableRendererFeature : ScriptableRendererFeature { private SampleLWRPScriptableRenderPass currentPass; public override void Create() { if (currentPass == null) currentPass = new SampleLWRPScriptableRenderPass(); } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { currentPass.SetRenderTarget(renderer.cameraColorTarget); renderer.EnqueuePass(currentPass); } }
currentPass.SetRenderTarget(renderer.cameraColorTarget);
SampleLWRPScriptableRenderPass に現在の描画ターゲットを予め教えてあげることで,CameraTarget 以外のレンダーターゲットがパイプライン内で指定されていても問題なく動くようにします。

SampleLWRPScriptableRenderPass.cs

using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.LWRP; public sealed class SampleLWRPScriptableRenderPass : ScriptableRenderPass { private const string Tag = "SampleRenderPass"; private RenderTargetIdentifier currentTarget; public SampleLWRPScriptableRenderPass() { renderPassEvent = RenderPassEvent.AfterRenderingSkybox; } public void SetRenderTarget(RenderTargetIdentifier target) { currentTarget = target; } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { var commandBuffer = CommandBufferPool.Get(Tag); var renderTextureId = Shader.PropertyToID("_SampleLWRPScriptableRenderer"); var material = new Material(Shader.Find("Hidden/SampleLWRPScriptableRenderer")); var cameraData = renderingData.cameraData; var w = cameraData.camera.scaledPixelWidth; var h = cameraData.camera.scaledPixelHeight; commandBuffer.GetTemporaryRT(renderTextureId, w, h, 0, FilterMode.Point, RenderTextureFormat.Default); commandBuffer.Blit(currentTarget, renderTextureId); commandBuffer.Blit(renderTextureId, currentTarget, material); context.ExecuteCommandBuffer(commandBuffer); CommandBufferPool.Release(commandBuffer); } }
renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
描画タイミングをここでは,SkyBox のレンダリング直後に指定しています。
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
このコールバック内で Camera Effect のやり方と同様に,CommandBuffer を組み立てます。LWRP のパスでは,CommandBuffer を CommandBufferPool より取得し,使い終わったら Release するようにします。 また,レンダーテクスチャ生成時に -1, -1 といった形でテクスチャサイズの相対指定はできません。 LWRP の RenderScale 機能に対応するために,Camera の scaledPixelWidth / Height を使います。
注意するのはこの点くらいで,Standard Render Pipeline の拡張用コードをほぼそのまま移植できそうですね。

SampleLWRPScriptableRenderer.shader

シェーダーは,[Create] -> [Shader] -> [Image Effect Shader] で生成した素の状態で LWRP 環境でも問題なく動作します。一応コードを載せておきます。
Shader "Hidden/SampleLWRPScriptableRenderer" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Cull Off ZWrite Off ZTest Always Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } sampler2D _MainTex; fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); // just invert the colors col.rgb = 1 - col.rgb; return col; } ENDCG } } }

LWRP に ScriptableRendererFeature を登録する

ScriptableRendererFeature アセットを生成する

右クリックメニューの Create から ScriptableRendererFeature を作成します。

Custom Forward Renderer アセットを生成する

同様に Create -> Rendering から,Forward Renderer アセットを作成します。

ScriptableRendererFeature を Custom Forward Renderer に登録する

Forward Renderer のインスペクタから,様々な Feature をプラグイン式に追加削除することができます。ここで,先程作成した ScriptableRendererFeature を追加します。

LWRP に Custom Forward Renderer を指定する

LWRP のパイプラインアセットインスペクタ内で,先程作成した Renderer を登録することができます。Renderer Type を Custom に切り替えて,Data に Custom Forward Renderer を指定します。
これで,登録作業は完了です。

完成

無事パイプラインを拡張することができました!
LWRP は Preview がはずれて,パイプラインの拡張が柔軟にできるようになっていました。カメラエフェクトに関しては,既存の資産を LWRP 対応することも大変ではなさそうです。
2019-05-04 mewlist
Hidenori Doi
Aiming Inc. Lead Software Engineer / Manager - Manager
11
Comments