本篇文章主要介紹URP自定義渲染RendererFeature、RendererPass的使用,并詳細解析繼承下來的每個方法什麼時候會被調用,基礎的URP請參考這位部落客文章:https://blog.csdn.net/qq_30259857/article/details/108318528。
測試demo效果
RendererFeature、RendererPass使用
在使用URP的時候先在PackageManager上導入Universal RP
1、當RendererFeature、RendererPass建立好(怎麼搞後面說)
2、在Assets中建立URP配置資産,右鍵Assets/Create/Rendering/Universal Render Pipeline/Pipeline Asset
3、然後再Assets上會出現2個資産檔案,在AddRendererFeature上添加第一步建立的RendererFeature,這樣這個feature才會被這個配置資産所激活
4、在ProjectSettings上将URP配置加到渲染管線設定,這樣系統将會切換到URP管線渲染,渲染配置使用該配置。
RendererFeature、RendererPass詳解
如何使用(先知道怎麼用,後面詳解繼承的每個方法):
1、建立Feature并繼承ScriptableRendererFeature,實作Create方法,RenderFeature被建立的時候調用,我們在這裡建立RendererPass,這裡public成員會被顯示在配置面闆上。
2、AddRenderPasses,這個每幀都會被調用,我們在這裡将Pass添加到渲染隊列裡面,
3、建立的Pass需要繼承ScriptableRenderPass,在構造函數我們設定一下這個Pass在哪個階段被執行,同時我們初始化一張RT用來本次案例使用。
4、Pass腳本中,Exceute函數會在執行這個Pass的時候調用,這裡我們建立一張RT,然後将目前螢幕顯示的内容通過我們的着色器修改後輸出到建立的RT上,然後再輸出回螢幕,最後執行這些操作。在所有渲染結束的時候FrameCleanup方法會被調用,在這裡将之前建立的RT登出掉。
RendererFeature詳解
繼承ScriptableRendererFeature這個類建立Feature,可以實作3個方法,它們的執行順序:Create、AddRenderPasses、Dispose。
1.void Create()
在父類上在OnEnable和OnValidate上調用,OnValidate是腳本被加載或Inspector 中的任何值被修改時調用。
2.void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
在UniversalRenderPipeline腳本中RenderSingleCamera中開始調用, RenderSingleCamera就是渲染單個錄影機的内容。
然後是ForwardRenderer腳本,這個腳本就是管線配置的RendererList所處理的内容,這裡可以是延遲渲染等其他自定義的渲染。
在ForwardRenderer裡有兩種情況,一種是隻渲染不透明物體和半透明物體的情況,這個時候就先将自定義的feature加入隊列,然後分别将不透明、天空、半透明加入隊列。
還有一種就是正常流程情況,就直接加入隊列啦,排序渲染的時候會根據枚舉排序的。
這是ForwardRenderer腳本父類ScriptableRenderer的AddRenderPasses方法,在這裡執行。
參數ScriptableRenderer,腳本解釋是渲染器,執行渲染操作、剔除等。
參數RenderingData,存儲了相機相關的資料、剔除結果、燈光資料等。
3. void Dispose(bool disposing)
跟蹤到最開始有3中情況調用,都在UniversalRenderPipelineAsset管線配置處理的腳本中。
首先管線腳本被加載或有任何更改的時候調用
管線腳本不激活的時候調用
在所有Feature被建立之前調用,也就是說Create之前會先調用,參數永遠為true,官方這麼傳遞,日後可能會有其他用途。
RendererPass詳解
繼承ScriptableRenderPass這個類,可以實作6個方法,它們的執行順序:OnCameraSetup、 Configure、Execute、FrameCleanup、OnCameraCleanup、OnFinishCameraStackRendering
1.OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
第一個被執行,可以看到,在相機等資料剛準備好的時候調用,其意思很明顯就是是不是有一些資料要對RenderingData做修改或填充。
2. void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
在執行RenderPass渲染的時候首先被執行,其實在這裡就可以開始插入渲染了(CommandBuffer)。RenderTextureDescriptor從字面上就知道是目前相機渲染了什麼在螢幕上的一張RT。
Unity将所有RenderPass分成了4個塊分批渲染,這不在本篇文章讨論範圍。
3. void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
在執行完所有Pass的Configure後立馬就執行Execute操作,我們所有的自定義渲染都在這裡面操作。ScriptableRenderContext是執行渲染操作的一個類,URP上裁剪完後的資料其實是這個腳本在管理,而不是ScriptableCullingParameters。
4. void FrameCleanup(CommandBuffer cmd)
在執行完所有Pass的Exceute後調用,也就是渲染完畢。
5. void OnCameraCleanup(CommandBuffer cmd)
他是FrameCleanup執行完後立刻執行。
6. void OnFinishCameraStackRendering(CommandBuffer cmd)
他是所有Pass的Cleanup執行完後最後才執行。
Demo代碼:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;
public class RendererFeatureTest : ScriptableRendererFeature
{
//會顯示在資産面闆上
public Color m_Color = Color.red;
RendererPassTest m_Pass;
//feature被建立時調用
public override void Create()
{
m_Pass = new RendererPassTest();
}
//每一幀都會被調用
public override void AddRenderPasses(UnityEngine.Rendering.Universal.ScriptableRenderer renderer, ref RenderingData renderingData)
{
//将目前渲染的顔色RT傳到Pass中
m_Pass.Setup(renderer.cameraColorTarget, m_Color);
//将這個pass添加到渲染隊列
renderer.EnqueuePass(m_Pass);
}
//一幀渲染完最後調用
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class RendererPassTest : ScriptableRenderPass
{
Color m_Color;
Material m_Mat = new Material(Shader.Find("Hidden/URPBaseTest"));
//RT的Filter
public FilterMode filterMode { get; set; }
//目前階段渲染的顔色RT
RenderTargetIdentifier m_Source;
//輔助RT
RenderTargetHandle m_TemporaryColorTexture;
//Profiling上顯示
ProfilingSampler m_ProfilingSampler = new ProfilingSampler("URPTest");
public RendererPassTest()
{
//在哪個階段插入渲染
renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
//初始化輔助RT名字
m_TemporaryColorTexture.Init("URPBaseTest");
}
public void Setup(RenderTargetIdentifier source, Color color)
{
m_Source = source;
m_Color = color;
m_Mat.SetColor("_Color", m_Color);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get();
//using的做法就是可以在FrameDebug上看到裡面的所有渲染
using (new ProfilingScope(cmd, m_ProfilingSampler))
{
//建立一張RT
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0;
cmd.GetTemporaryRT(m_TemporaryColorTexture.id, opaqueDesc, filterMode);
//将目前幀的顔色RT用自己的着色器渲處理然後輸出到建立的貼圖上
Blit(cmd, m_Source, m_TemporaryColorTexture.Identifier(), m_Mat);
//将處理後的RT重新渲染到目前幀的顔色RT上
Blit(cmd, m_TemporaryColorTexture.Identifier(), m_Source);
}
//執行
context.ExecuteCommandBuffer(cmd);
//回收
CommandBufferPool.Release(cmd);
}
public override void FrameCleanup(CommandBuffer cmd)
{
base.FrameCleanup(cmd);
//銷毀建立的RT
cmd.ReleaseTemporaryRT(m_TemporaryColorTexture.id);
}
}
Shader "Hidden/URPBaseTest"
{
Properties
{
_MainTex("ScreenTexture", 2D) = "white" {}
_Color("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#pragma vertex vert
#pragma fragment frag
struct Attributes
{
float4 positionOS : POSITION;
float2 uv:TEXCOORD0;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv:TEXCOORD0;
};
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
Varyings vert(Attributes v)
{
Varyings o = (Varyings)0;
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.positionOS.xyz);
o.positionCS = vertexInput.positionCS;
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
half4 frag(Varyings i) : SV_Target
{
half4 col = tex2D(_MainTex, i.uv);
return lerp(col, _Color, 0.2);
}
ENDHLSL
}
}
}