|
一、思路
在unity urp中,unity官方不像在build in管线种一样提供了后处理的接口,所以需要借助render feature来实现后处理效果。
总的思路就是,render feature可以获取到某一时刻绘制出来的图像(比如全部不透明物体,或者全部物体绘制后),之后获取到这个图像进行处理,再将处理的结果放回到渲染管线中。
1.1流程
首先要建立一个类继承ScriptableRendererFeature
public class BrightnessPosetProcessing : ScriptableRendererFeature
{
//...
}然后创建一个自己的pass类
public class BrightnessPosetProcessing : ScriptableRendererFeature
{
//*************************************************
//********** 自定义Pass类 **************
//**************************************************
class CustomRenderPass : ScriptableRenderPass
{
//....
}
}之后在Create接口中,对自己的Pass进行初始化(这里也是最开始运行的地方),注意pass的声明要写在函数外
private CustomRenderPass pass;
//*************************************************
//********** 初始化 **************
//**************************************************
public override void Create()
{
pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
}最后,获取相机输出的图像,使用该pass处理,然后将这个pass添加进渲染管线中
private CustomRenderPass pass;
//*************************************************
//********** 初始化 **************
//**************************************************
public override void Create()
{
pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
}
//*************************************************
//********** 将pass添加到渲染管线 **************
//**************************************************
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
//获取当前相机中的图像
var src = renderer.cameraColorTarget;
pass.SetUp(src);
//将该pass添加到渲染管线中
renderer.EnqueuePass(pass);
}1.2核心实现
以上流程中的核心是自定义pass的实现,而自定义pass中的核心是Execute函数的实现,这个函数会在每一帧绘制的时候被调用。
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
//从命令缓冲区池获取一个带标签的命令缓冲区,该标签名在帧调试器中可以见到
CommandBuffer cmd = CommandBufferPool.Get(passTag);
//获取目标相机的描述信息,创建一个结构体,里面有render texture各中信息,比如尺寸,深度图精度等等
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
//设置深度缓冲区,0表示不需要深度缓冲区
opaqueDesc.depthBufferBits = 0;
//申请临时图像
cmd.GetTemporaryRT(passTempleColorTex.id, opaqueDesc);
//将源图像放入材质中计算,然后存储到临时缓冲区中
cmd.Blit(passSource, passTempleColorTex.Identifier(), passMat);
//将临时缓冲区的结果存回源图像中
cmd.Blit(passTempleColorTex.Identifier(), passSource);
//执行命令缓冲区
context.ExecuteCommandBuffer(cmd);
//释放命令缓存
CommandBufferPool.Release(cmd);
//释放临时render texture
cmd.ReleaseTemporaryRT(passTempleColorTex.id);
}二、代码
SimplePostProcessing.shader
Shader "Unlit/16_SimplePostProcessing"
{
Properties
{
[HideInInspector]_MainTex ("Texture", 2D) = "white" {}
_Brightness("Brightness", Range(0,1)) = 1
_Saturate("Saturate", Range(0,1)) = 1
_Contrast("Constrast", Range(-1,2)) = 1
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalRenderPipeline"
}
Cull Off
ZWrite Off
ZTest Always
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float _Brightness;
float _Saturate;
float _Contrast;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
struct Attributes
{
float4 positionOS: POSITION;
float2 texcoord: TEXCOORD0;
};
struct Varyings
{
float4 positionCS:SV_POSITION;
float2 uv:TEXCOORD0;
};
ENDHLSL
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS);
output.uv = input.texcoord;
return output;
}
half4 frag (Varyings input) : SV_Target
{
half4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
float grey = 0.21*tex.x+0.72*tex.y+0.072*tex.z;
tex.xyz *= _Brightness;
tex.xyz = lerp(float3(grey,grey,grey), tex.xyz, _Saturate);
tex.xyz = lerp(float3(0.5,0.5,0.5),tex.xyz, _Contrast);
return tex;
}
ENDHLSL
}
}
}
BrightnessPostProcessing.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class BrightnessPosetProcessing : ScriptableRendererFeature
{
//定义配置类
[System.Serializable]
public class Setting
{
//默认在透明物体绘制完成后进行处理
public RenderPassEvent passEvent = RenderPassEvent.AfterRenderingTransparents;
//后续需要用到的材质
public Material mat;
}
public Setting setting = new Setting();
//*************************************************
//********** 自定义Pass类 **************
//**************************************************
class CustomRenderPass : ScriptableRenderPass
{
//后处理用到的材质
public Material passMat = null;
private RenderTargetIdentifier passSource { get; set; }//源图像,目标图像
private RenderTargetHandle passTempleColorTex;//临时计算的图像
//标签名,用于在帧调试器中显示缓冲区的名称
private string passTag;
//构造函数
public CustomRenderPass(RenderPassEvent passEvent, Material material, string tag)
{
this.renderPassEvent = passEvent;
this.passMat = material;
passTag = tag;
}
//设置Pass
public void SetUp(RenderTargetIdentifier source)
{
this.passSource = source;
}
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
//从命令缓冲区池获取一个带标签的命令缓冲区,该标签名在帧调试器中可以见到
CommandBuffer cmd = CommandBufferPool.Get(passTag);
//获取目标相机的描述信息,创建一个结构体,里面有render texture各中信息,比如尺寸,深度图精度等等
RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
//设置深度缓冲区,0表示不需要深度缓冲区
opaqueDesc.depthBufferBits = 0;
//申请临时图像
cmd.GetTemporaryRT(passTempleColorTex.id, opaqueDesc);
//将源图像放入材质中计算,然后存储到临时缓冲区中
cmd.Blit(passSource, passTempleColorTex.Identifier(), passMat);
//将临时缓冲区的结果存回源图像中
cmd.Blit(passTempleColorTex.Identifier(), passSource);
//执行命令缓冲区
context.ExecuteCommandBuffer(cmd);
//释放命令缓存
CommandBufferPool.Release(cmd);
//释放临时render texture
cmd.ReleaseTemporaryRT(passTempleColorTex.id);
}
}
private CustomRenderPass pass;
//*************************************************
//********** 初始化 **************
//**************************************************
public override void Create()
{
pass = new CustomRenderPass(setting.passEvent, setting.mat, name);
}
//*************************************************
//********** 将pass添加到渲染管线 **************
//**************************************************
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
//获取当前相机中的图像
var src = renderer.cameraColorTarget;
pass.SetUp(src);
//将该pass添加到渲染管线中
renderer.EnqueuePass(pass);
}
}
三、结果
四、参考
[笔记]URP-屏幕后处理
[Unity]为了更好用的后处理——扩展URP后处理踩坑记录
urp管线的自学hlsl之路 第十七篇 建立自己的后处理系统并调整屏幕的亮度饱和度对比度 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|