本篇文章整理了URP管線使用中的一些簡單的心得記述
1.使用ScriptableRendererFeature自定義渲染特性
在內建(Build-in)管線中可以使用CommandBuffer並添加到攝像機上來實現自定義的特性。在URP管線中,處理方法變成了RendererFeature
RendererFeature不需要綁定到相機;而是掛載到渲染器(如ForwardRenderer)的設置里。
在Project面板點擊右鍵Create/Rendering/Universal Render Pipeline/Renderer Feature可以創建Renderer Feature模板。
模板中Feature帶有一個CustomRenderPass的嵌套類;並且在AddRenderPasses函數中被添加進pass隊列。
ScriptableRenderPass可以指定需要的渲染步驟,如:
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
由於一個效果往往需要多個pass不同階段處理;這樣的方式顯然比較友好。
在之前內建的渲染管線中;CommandBuffer只能執行預先設定好的一些步驟,這樣多少有些不方便。在自定義管線中CommandBuffer變成了立即執行,
現在可以在ScriptableRenderPass中直接通過context來立即執行CommandBuffer:
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get("MyCommandBuffer"); //CommandBuffer的操作 context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); }
此外,標記Profiling后,可在FrameDebugger中直接查看標記Profiling的對象:
{ ProfilingSampler mProfilingSampler = new ProfilingSampler("Test1"); CommandBuffer cmd = CommandBufferPool.Get("Test1 Cmd"); using (new ProfilingScope(cmd, mProfilingSampler)) { MeshRenderer meshRenderer = Resources.Load<MeshRenderer>("TestModel"); cmd.DrawRenderer(meshRenderer, meshRenderer.sharedMaterial); context.ExecuteCommandBuffer(cmd); CommandBufferPool.Release(cmd); } }
而打開了管線設置中的Debug Level后;可以通過這個參數看見更多的調試信息
如UniversalRenderPipeline.cs的RenderSingleCamera方法里:
static void RenderSingleCamera(...)
{
...
asset.debugLevel >= PipelineDebugLevel.Profiling ? ...
這段代碼在勾選這個設置后可以在FrameDebugger內顯示不同的相機名。
2.使用DrawRenderers進行大批量繪制
在內建管線中,通常使用CommandBuffer.DrawRenderer來繪制一些指定的對象,
不過繪制對象一多這樣做就不太方便。
在URP的自定義pass中可以使用ScriptableRenderContext上下文里的DrawRenderers接口進行批量繪制,
它可以拿到當前相機的剔除結果(CullingResults),通過FilteringSettings參數再進行一次過濾。
FilteringSettings里還可以設置renderingLayerMask,renderingLayerMask可在MeshRenderer、SkinnedMeshRenderer
等渲染器組件中設置,獨立於舊的Layer。
借助傳入的DrawingSettings,RenderStateBlock參數可指定是否寫入Stencil、是否寫深度等信息,最終完成繪制,如:
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref m_FilteringSettings, ref m_RenderStateBlock);
如果要繪制的內容第一次相機剔除時沒有,也可以再進行一次相機剔除:
context.Cull(...)
使用新的結果來繪制。
以前抓取uGUI的繪制內容較為困難,現在也可以用這種辦法把UI分成幾部分繪制,並且控制每一部分是否寫入指定RT了,
並且還可以通過自定義的Feature和context.DrawRenderers把UI繪制的步驟單獨挪出來自行控制(但是Stencil會丟掉,酌情使用)。
還可以先修改RenderTarget再執行context.DrawRenderers繪制,這樣就可以把內容批量繪制到指定RT上:
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { CommandBuffer cmd = CommandBufferPool.Get("tempRt"); int tmpRt = Shader.PropertyToID("_TempRt"); cmd.GetTemporaryRT(tmpRt, mDesc); cmd.SetRenderTarget(tmpRt); context.ExecuteCommandBuffer(cmd); context.DrawRenderers(...); CommandBufferPool.Release(cmd); }
(這里的RenderTexture應該在Configure中綁定)
注意,內建管線中使用CommandBuffer時可以直接填寫-1、-2等寬高參數獲得1/2,1/3等大小的RenderTexture,在自定義管線中
不再可用。
更多的繪制方法可以參考Render Objects(Runtime/RendererFeatures/RenderObjects.cs)或
DrawObjectsPass.cs(Runtime/Passes/DrawObjectsPass.cs)的做法。
3.相較內建管線的優化
在URP中單個對象支持的最大燈光數量是8盞;光照處理的操作在一個pass中完成。
雖然有數量限制;但這樣整個場景的Batches數量得到了控制,這一點體現較為明顯。
而陰影方面目前只能支持主光源平行光的陰影和聚光燈的陰影(v7.3.1)。
其次就是SRP Batches等技術,這方面暫未深入了解。