URP(Universal Render Pipeline)渲染管线在使用中的一些分享

本篇文章整理了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等技术,这方面暂未深入了解。

原文地址:https://www.cnblogs.com/hont/p/14221019.html