天天看點

Unity URP渲染管線---自定義渲染詳解(入門)

    本篇文章主要介紹URP自定義渲染RendererFeature、RendererPass的使用,并詳細解析繼承下來的每個方法什麼時候會被調用,基礎的URP請參考這位部落客文章:https://blog.csdn.net/qq_30259857/article/details/108318528。

測試demo效果

Unity URP渲染管線---自定義渲染詳解(入門)
Unity URP渲染管線---自定義渲染詳解(入門)

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才會被這個配置資産所激活

Unity URP渲染管線---自定義渲染詳解(入門)

4、在ProjectSettings上将URP配置加到渲染管線設定,這樣系統将會切換到URP管線渲染,渲染配置使用該配置。

Unity URP渲染管線---自定義渲染詳解(入門)

RendererFeature、RendererPass詳解

如何使用(先知道怎麼用,後面詳解繼承的每個方法):

1、建立Feature并繼承ScriptableRendererFeature,實作Create方法,RenderFeature被建立的時候調用,我們在這裡建立RendererPass,這裡public成員會被顯示在配置面闆上。

Unity URP渲染管線---自定義渲染詳解(入門)

2、AddRenderPasses,這個每幀都會被調用,我們在這裡将Pass添加到渲染隊列裡面,

Unity URP渲染管線---自定義渲染詳解(入門)

3、建立的Pass需要繼承ScriptableRenderPass,在構造函數我們設定一下這個Pass在哪個階段被執行,同時我們初始化一張RT用來本次案例使用。

Unity URP渲染管線---自定義渲染詳解(入門)

4、Pass腳本中,Exceute函數會在執行這個Pass的時候調用,這裡我們建立一張RT,然後将目前螢幕顯示的内容通過我們的着色器修改後輸出到建立的RT上,然後再輸出回螢幕,最後執行這些操作。在所有渲染結束的時候FrameCleanup方法會被調用,在這裡将之前建立的RT登出掉。

Unity URP渲染管線---自定義渲染詳解(入門)

RendererFeature詳解

繼承ScriptableRendererFeature這個類建立Feature,可以實作3個方法,它們的執行順序:Create、AddRenderPasses、Dispose。

1.void Create()

      在父類上在OnEnable和OnValidate上調用,OnValidate是腳本被加載或Inspector 中的任何值被修改時調用。

Unity URP渲染管線---自定義渲染詳解(入門)

2.void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)

     在UniversalRenderPipeline腳本中RenderSingleCamera中開始調用,  RenderSingleCamera就是渲染單個錄影機的内容。

Unity URP渲染管線---自定義渲染詳解(入門)

     然後是ForwardRenderer腳本,這個腳本就是管線配置的RendererList所處理的内容,這裡可以是延遲渲染等其他自定義的渲染。

Unity URP渲染管線---自定義渲染詳解(入門)

     在ForwardRenderer裡有兩種情況,一種是隻渲染不透明物體和半透明物體的情況,這個時候就先将自定義的feature加入隊列,然後分别将不透明、天空、半透明加入隊列。

Unity URP渲染管線---自定義渲染詳解(入門)

     還有一種就是正常流程情況,就直接加入隊列啦,排序渲染的時候會根據枚舉排序的。

Unity URP渲染管線---自定義渲染詳解(入門)

     這是ForwardRenderer腳本父類ScriptableRenderer的AddRenderPasses方法,在這裡執行。

Unity URP渲染管線---自定義渲染詳解(入門)

參數ScriptableRenderer,腳本解釋是渲染器,執行渲染操作、剔除等。

參數RenderingData,存儲了相機相關的資料、剔除結果、燈光資料等。

3. void Dispose(bool disposing)

跟蹤到最開始有3中情況調用,都在UniversalRenderPipelineAsset管線配置處理的腳本中。

首先管線腳本被加載或有任何更改的時候調用

Unity URP渲染管線---自定義渲染詳解(入門)

管線腳本不激活的時候調用

Unity URP渲染管線---自定義渲染詳解(入門)

在所有Feature被建立之前調用,也就是說Create之前會先調用,參數永遠為true,官方這麼傳遞,日後可能會有其他用途。

Unity URP渲染管線---自定義渲染詳解(入門)

RendererPass詳解

繼承ScriptableRenderPass這個類,可以實作6個方法,它們的執行順序:OnCameraSetup、 Configure、Execute、FrameCleanup、OnCameraCleanup、OnFinishCameraStackRendering

1.OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)

第一個被執行,可以看到,在相機等資料剛準備好的時候調用,其意思很明顯就是是不是有一些資料要對RenderingData做修改或填充。

Unity URP渲染管線---自定義渲染詳解(入門)

2. void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)

    在執行RenderPass渲染的時候首先被執行,其實在這裡就可以開始插入渲染了(CommandBuffer)。RenderTextureDescriptor從字面上就知道是目前相機渲染了什麼在螢幕上的一張RT。

Unity URP渲染管線---自定義渲染詳解(入門)

Unity将所有RenderPass分成了4個塊分批渲染,這不在本篇文章讨論範圍。

Unity URP渲染管線---自定義渲染詳解(入門)

3. void Execute(ScriptableRenderContext context, ref RenderingData renderingData)

    在執行完所有Pass的Configure後立馬就執行Execute操作,我們所有的自定義渲染都在這裡面操作。ScriptableRenderContext是執行渲染操作的一個類,URP上裁剪完後的資料其實是這個腳本在管理,而不是ScriptableCullingParameters。

Unity URP渲染管線---自定義渲染詳解(入門)

4. void FrameCleanup(CommandBuffer cmd)

在執行完所有Pass的Exceute後調用,也就是渲染完畢。

Unity URP渲染管線---自定義渲染詳解(入門)

5. void OnCameraCleanup(CommandBuffer cmd)

    他是FrameCleanup執行完後立刻執行。

Unity URP渲染管線---自定義渲染詳解(入門)

6. void OnFinishCameraStackRendering(CommandBuffer cmd)

    他是所有Pass的Cleanup執行完後最後才執行。

Unity URP渲染管線---自定義渲染詳解(入門)

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
		}
	}
}
           

繼續閱讀