1.簡介
隨著Unity6的釋出,URP17也已經可以上手使用,相對舊的版本改動較大的是加入了
RenderGraph、STP、Foveated rendering、GPU Resident Drawer等功能,部分功能只需要開關引數即可使用,
而GRD更像是Gpu driven管線下的SRP Batches升級,RenderGraph相較於HDRP之前使用的版本換了一套API。
最大的不同是,使用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組織相關邏輯。
在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.2f; 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 UnityEngine; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule.Util; public class MyBlurPass : ScriptableRenderPass { public class PassData { public TextureHandle tempRt1; public TextureHandle tempRt2; } 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 passData = new PassData(); var w = Screen.width >> downSample; var h = Screen.height >> downSample; RenderTextureDescriptor textureProperties = new RenderTextureDescriptor(w, h, RenderTextureFormat.Default, 0); passData.tempRt1 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "MyBlurPassTempRt1", false); textureProperties = new RenderTextureDescriptor(w, h, RenderTextureFormat.Default, 0); passData.tempRt2 = UniversalRenderer.CreateRenderGraphTexture(renderGraph, textureProperties, "MyBlurPassTempRt2", false); var rt1 = passData.tempRt1; var rt2 = passData.tempRt2; //將螢幕RT Blit到rt1上 var para = new RenderGraphUtils.BlitMaterialParameters(resourceData.activeColorTexture, rt1, material, 0); renderGraph.AddBlitPass(para, "MyBlurPassBlitFirst"); material.SetFloat("_SampleOffset", offset); //模糊迭代 for (int i = 0; i < blurPasses - 1; ++i) { para = new RenderGraphUtils.BlitMaterialParameters(rt1, rt2, material, 0); renderGraph.AddBlitPass(para, $"MyBlurPassBlit_{i}"); var tmp = rt1; rt1 = rt2; rt2 = tmp; } //透過直接繪製的方式,將模糊RT繪製到螢幕上 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); }); } } }
接著在ShaderGraph中連出模糊的邏輯,注意Blit對應的引數_BlitTexture、_BlitScaleBias:
最後在場景中掛載控制器以及材質球,即可使用該模糊Pass。