概述
本来想写这个很久了,但是一直都在忙别的。
最近项目也需要用到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;
同理,在运行游戏的时候,在Execute方法里,可以通过判断某个类的静态变量来开启或关闭自定义后处理的功能。
if(!RenderManager.OpenPostEffect)return;
代码下载:
链接:https://pan.baidu.com/s/1V85II6YYsVx_LFjWI1aseg
提取码:gjww
参考文章:
https://github.com/Unity-Technologies/UniversalRenderingExamples