概述
本來想寫這個很久了,但是一直都在忙別的。
最近項目也需要用到URP的後處理,但是不一定有想要的後處理效果。所以有些還是得自己寫。
但是URP的後處理和之前unity的後處理寫法完全不一樣了。原來的OnRenderImage、OnPreRender都失效了。
本文只探討如何寫URP下的自定義後處理,並非討論具體的渲染效果,這裏我只做了修改對比度的屏幕特效。
具體實現
首先需要創建一個自定義的c#的ScriptableRendererFeature和ScriptableRenderPass的類。
然後在ScriptableRendererFeature類裏寫一個Setting類,並實例化它。其中 [System.Serializable]是必須的。類的命名不需要講究。
[System.Serializable]
public class HLSettings
{
public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
public Material mMat;
public Target destination = Target.Color;
public int blitMaterialPassIndex = -1;
//這是一個shader的propertyId
public string textureId = "_ScreenTexture";
public float contrast = 0.5f;
}
public HLSettings settings = new HLSettings();
這樣在PipelineAsset 點擊Add Renderer Feature後,可以明顯的看到setting的內容,並可以設置它們。
接着,在ScriptableRendererFeature的Create方法裏實例化ScriptableRenderPass類。
RenderTargetHandle m_renderTargetHandle;
HLRenderPass m_ScriptablePass;
public override void Create()
{
int passIndex = settings.mMat != null ? settings.mMat.passCount - 1 : 1;
settings.blitMaterialPassIndex = Mathf.Clamp(settings.blitMaterialPassIndex, -1, passIndex);
m_ScriptablePass = new HLRenderPass("HLPostEffectRender", settings.renderPassEvent, settings.mMat, settings.contrast);
m_renderTargetHandle.Init(settings.textureId);
}
再接着,在AddRenderPasses方法裏設置ScriptableRenderPass的source和destination的renderTarget。
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
var src = renderer.cameraColorTarget;
var dest = (settings.destination == Target.Color) ? RenderTargetHandle.CameraTarget : m_renderTargetHandle;
if (settings.mMat == null)
{
Debug.LogWarningFormat("丟失blit材質");
return;
}
m_ScriptablePass.Setup(src,dest);
renderer.EnqueuePass(m_ScriptablePass);
}
這樣,ScriptableRendererFeature類就寫好了。
接着我們寫ScriptableRenderPass類。首先寫它的構造函數。注意,this.renderPassEvent必須正確賦值才能保證該類在正確
的RenderPassEvent的順序下渲染。比如可以選擇RenderPassEvent.AfterRenderingOpaques或者RenderPassEvent.AfterRenderingSkybox才執行該pass裏的Execute方法。
public HLRenderPass(string passname, RenderPassEvent _event, Material _mat,float contrast)
{
m_ProfilerTag = passname;
this.renderPassEvent = _event;
mMat = _mat;
mMat.SetFloat("_Contrast", contrast);
m_temporaryColorTexture.Init("temporaryColorTexture");
}
再接着,寫一個接口,把source和destination的renderTarget傳入進來。
public void Setup(RenderTargetIdentifier src, RenderTargetHandle dest)
{
this.source = src;
this.destination = dest;
}
緊接着,Execute方法裏執行CommandBuffer的方法,也就是以前非URP環境下的CommandBuffer寫法類似。
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
opaqueDesc.depthBufferBits = 0;
//不能讀寫同一個顏色target,創建一個臨時的render Target去blit
if (destination == RenderTargetHandle.CameraTarget)
{
cmd.GetTemporaryRT(m_temporaryColorTexture.id, opaqueDesc, filterMode);
Blit(cmd, source, m_temporaryColorTexture.Identifier(), mMat, blitShaderPassIndex);
Blit(cmd, m_temporaryColorTexture.Identifier(), source);
}
else
{
Blit(cmd, source, destination.Identifier(), mMat, blitShaderPassIndex);
}
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
然後,在FrameCleanup方法裏做一些釋放臨時資源的工作,ScriptableRenderPass類的大體處理就到這了。
public override void FrameCleanup(CommandBuffer cmd)
{
if (destination == RenderTargetHandle.CameraTarget)
cmd.ReleaseTemporaryRT(m_temporaryColorTexture.id);
}
最後,在PipelineAsset 點擊Add Renderer Feature後添加自己寫的Renderer Feature,並在Settings裏賦值,最重要是
材質的賦值,該材質決定了你的屏幕特效的效果是怎麼渲染的。
溫馨提示:
在Execute方法裏可獲取cameraData.postProcessEnabled是否生效,從而判斷是否需要執行後處理效果。
if (!renderingData.cameraData.postProcessEnabled) return;
代碼下載:
鏈接:https://pan.baidu.com/s/1V85II6YYsVx_LFjWI1aseg
提取碼:gjww
參考文章:
https://github.com/Unity-Technologies/UniversalRenderingExamples