Unity6 URP17使用初探
1.简介
随着Unity6的发布,URP17也已经可以上手使用,相对旧的版本改动较大的是加入了
新的RenderGraph、STP、Foveated rendering、GPU Resident Drawer等功能,部分功能只需要开关参数即可使用,
而GPU Resident Drawer好比BRG(BatchRendererGroup)的升级,RenderGraph相较HDRP那一版也有诸多差异。
最大的不同是,使用URP17编写Feature时,必须依赖于RenderGraph进行编写,接下来就来介绍一下。
1.1 相关Demo
目前URP17比较容易找到的学习Demo如下:
- URP17 Package内自带的Samples
- Fantasy Kingdom (https://assetstore.unity.com/packages/essentials/tutorial-projects/fantasy-kingdom-in-unity-6-urp-298128)
- 在Unity Hub可以直接下载的Universal 3D sample(包含3个场景)
2.RenderGraph
打开任意URP的示例场景查看,RenderGraphView上各图标含义如下:
- 说明这是一个外部置入的RenderTexture
- 红色方块说明存在写入操作
- 绿色方块指存在读取操作(红绿方块说明读写操作)
- 该图标说明标记了全局RenderTexture
而顶部表明当前渲染一帧的各个Pass,左侧是各类RT。
URP17的一些案例同时保留了旧的Feature逻辑与RenderGraph逻辑(打开任意pass文件为例):
public class DistortTunnelPass_Tunnel : ScriptableRenderPass { class PassData { public Renderer tunnelObject; public Material tunnelMaterial; } #pragma warning disable 618, 672 // Type or member is obsolete, Member overrides obsolete member // Unity calls the Configure method in the Compatibility mode (non-RenderGraph path) public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescripor) { } // Unity calls the Execute method in the Compatibility mode public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { } #pragma warning restore 618, 672 // Unity calls the RecordRenderGraph method to add and configure one or more render passes in the render graph system. public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { } }
参考时忽略掉Configure和Execute的逻辑,执行逻辑关注RecordRenderGraph函数。
2.1 操作方式的改变
在RenderGraph中,之前的RTHandle由于不在该系统中托管,进入RenderGraph的材质都需要调用API进行转换,
转换为RendeGraph的RT后,无需考虑释放操作:
RenderTextureDescriptor textureProperties = new RenderTextureDescriptor(Screen.width, Screen.height, RenderTextureFormat.Default, 0); TextureHandle textureHandle = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "My texture", false);
相关文档:
https://docs.unity3d.com/Manual/urp/render-graph-create-a-texture.html
此外RenderGraph对于空调用的pass,也会剔除进行优化,使用者需要手动标记以防止被剔除。
2.1 RecordRenderGraph
在该函数内可组织渲染逻辑,pass相关的逻辑需放在对应的代码块中,例如:
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out _)) { builder.UseTexture(rt1); builder.SetRenderAttachment(resourceData.activeColorTexture, 0); builder.SetRenderFunc<PassData>((data, context) => { MaterialPropertyBlock materialPropertyBlock = new MaterialPropertyBlock(); materialPropertyBlock.SetTexture("_BlitTexture", rt1); materialPropertyBlock.SetVector("_BlitScaleBias", new Vector4(1, 1, 0, 0)); context.cmd.DrawProcedural(Matrix4x4.identity, material, 0, MeshTopology.Triangles, 3, 1, materialPropertyBlock); }); }
URP提供了多种RenderPass,例如处理光栅化相关逻辑使用RasterRenderPass组织相关逻辑,
涉及到调用旧API的逻辑,可以使用UnsafePass(https://docs.unity3d.com/6000.0/Documentation/Manual/urp/render-graph-unsafe-pass.html)
在RenderPass的代码块中可使用builder对象配置RenderTarget、标记材质的读写等
而具体的pass绘制逻辑则在SetRenderFunc代码块中。
RecordRenderGraph内可以调用多次AddRenderPass,但URP并没有对API进行整理,
以至于不小心混用了API就会报错,这点需要注意。
3.编写Feature
3.1 Blit与SetTarget
从前有句俗话“切RT的性能消耗相当于半个pass”,Unity SRP在几个版本的升级都在逐渐强调不切RenderTarget直接绘制,
如Cockpit Demo的屏幕空间描边。
3.2 屏幕模糊Demo
下面通过屏幕模糊Demo案例,演示URP17下pass的编写。
通过外部EnqueuePass的方式,在场景中通过控制器脚本添加该Pass,
MyBlurSceneController.cs:
using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering; public class MyBlurSceneController : MonoBehaviour { public Material material; [Range(2, 15)] public int blurPasses = 3; [Range(0, 4)] public int downSample = 0; [Range(0.0f, 10f)] public float offset = 0.02f; public RenderPassEvent injectionPoint = RenderPassEvent.BeforeRenderingPostProcessing; public int injectionPointOffset = 0; public ScriptableRenderPassInput inputRequirements = ScriptableRenderPassInput.Color; public CameraType cameraType = CameraType.Game; private MyBlurPass mMyBlurPass; private void OnEnable() { SetupPass(); RenderPipelineManager.beginCameraRendering += OnBeginCamera; } private void OnDisable() { RenderPipelineManager.beginCameraRendering -= OnBeginCamera; } public virtual void SetupPass() { mMyBlurPass = new MyBlurPass(); mMyBlurPass.renderPassEvent = injectionPoint + injectionPointOffset; mMyBlurPass.material = material; mMyBlurPass.ConfigureInput(inputRequirements); } public virtual void OnBeginCamera(ScriptableRenderContext ctx, Camera cam) { if (mMyBlurPass == null || material == null) return; if ((cam.cameraType & cameraType) == 0) return; mMyBlurPass.blurPasses = blurPasses; mMyBlurPass.downSample = downSample; mMyBlurPass.offset = offset; cam.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(mMyBlurPass); } }
MyBlurPass.cs:
using System; using UnityEngine; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; public class MyBlurPass : ScriptableRenderPass { private class PassData { public TextureHandle target; public TextureHandle source; public TextureHandle finalDrawTexture; public int blurPasses; public Material material; } public Material material; [Range(2, 15)] public int blurPasses = 3; [Range(1, 4)] public int downSample = 1; [Range(0.0f, 10f)] public float offset = 0.2f; public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { var resourceData = frameData.Get<UniversalResourceData>(); var w = Screen.width >> downSample; var h = Screen.height >> downSample; RenderTextureDescriptor textureProperties = new RenderTextureDescriptor(w, h, RenderTextureFormat.Default, 0); var rt1 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "MyBlurPassTempRt1", false); textureProperties = new RenderTextureDescriptor(w, h, RenderTextureFormat.Default, 0); var rt2 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "MyBlurPassTempRt2", false); //URP17还有一个接口renderGraph.AddBlitPass,但不能外部传PassData,因此目前版本只能用UnsafePass调用旧的Blit API来写 using (var builder = renderGraph.AddUnsafePass("Blur Pass Begin", out PassData passData, profilingSampler)) { passData.source = resourceData.activeColorTexture; passData.target = rt1; passData.material = material; builder.UseTexture(resourceData.activeColorTexture); builder.UseTexture(rt1, AccessFlags.Write); builder.SetRenderFunc((PassData data, UnsafeGraphContext context) => ExecuteUnsafeBlitPass(data, context)); } material.SetFloat("_SampleOffset", offset); using (var builder = renderGraph.AddUnsafePass("Blur Pass Iterate", out PassData passData, profilingSampler)) { passData.source = rt1; passData.target = rt2; passData.material = material; passData.blurPasses = blurPasses; builder.UseTexture(rt1, AccessFlags.ReadWrite); builder.UseTexture(rt2, AccessFlags.ReadWrite); builder.SetRenderFunc((PassData data, UnsafeGraphContext context) => ExecuteUnsafeBlitPassIterate(data, context)); } //通过直接绘制的方式,将模糊RT绘制到屏幕上 using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out PassData passData)) { passData.finalDrawTexture = rt1; builder.UseTexture(passData.finalDrawTexture); builder.SetRenderAttachment(resourceData.activeColorTexture, 0); builder.SetRenderFunc<PassData>((passData, context) => { MaterialPropertyBlock materialPropertyBlock = new MaterialPropertyBlock(); materialPropertyBlock.SetTexture("_BlitTexture", passData.finalDrawTexture); materialPropertyBlock.SetVector("_BlitScaleBias", new Vector4(1, 1, 0, 0)); context.cmd.DrawProcedural(Matrix4x4.identity, material, 0, MeshTopology.Triangles, 3, 1, materialPropertyBlock); }); } } private static void ExecuteUnsafeBlitPass(PassData passData, UnsafeGraphContext context) { CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); TextureHandle source = passData.source; TextureHandle target = passData.target; Blitter.BlitCameraTexture(unsafeCmd, source, target, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 0); } private static void ExecuteUnsafeBlitPassIterate(PassData passData, UnsafeGraphContext context) { CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); TextureHandle source = passData.source; TextureHandle target = passData.target; Blitter.BlitCameraTexture(unsafeCmd, source, target, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 0); for (int i = 0; i < passData.blurPasses; ++i) { Blitter.BlitCameraTexture(unsafeCmd, source, target, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 0); Blitter.BlitCameraTexture(unsafeCmd, target, source, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 0); } } }
接着在ShaderGraph中组合出模糊逻辑,注意Blit对应的参数_BlitTexture、_BlitScaleBias,采样贴图在SG中对应SAMPLE_TEXTURE2D:
最后在场景中挂载控制器以及材质球,即可使用该模糊Pass。