
主要是有3个模块组成
Volume 模块这个就是后处理组件模块

RenderFeature渲染器模块

Pass模块是编辑后处理核心代码类。
<aside> 💡 创建渲染管线
找到项目中正在使用的PiplineAsset文件(在Edit->ProjectSetting->Quality->Rendering下可以找到项目正在使用的渲染管线文件),如果没有的话,新建一个Pipline Asset(Project下,右键Create->Rendering->URP->Pipline Asset),创建完成后,项目中除了PiplineAsset文件,还会多出一个Forward Renderer文件,在Forward Renderer文件中点击Add Renderer Feature,就可以添加自定义的后处理效果了。
</aside>
渲染器

现在项目中没有自定义后处理效果,创建一个。
我们调整后处理是在Volume组件调整,所以我们需要定义一个脚本把参数穿过来。
新建一个脚本,命名ColorTint 继承 VolumeComponent类
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ColorTint : VolumeComponent
{
// 设置颜色参数
public ColorParameter ColorChange = new ColorParameter(Color.white, true);
} 注意:导入的命名空间是需要注意的。

效果

RenderFeature模块 是用来设置Shader以及渲染大体逻辑的,
首先,新建一个继承ScriptableRendererFeature的脚本,
ScriptableRendererFeature是URP提供给用户用来扩展自定义渲染功能的入口
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ColorTintRenderFeature: ScriptableRendererFeature
{
public override void Create()
{
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
}
} 注意:导入的命名空间 using UnityEngine.Rendering.Universal。
这就是这个类最简单的框架结构。
效果

这里我们就是把新建的类,在ForwardRenderer里增加使用。
扩展这个类里自带的俩个函数(Create 和 AddRenderPasses)是什么功能?
Create Feature被创建的时候调用,初始化。
AddRenderPasses 每帧都会调用,渲染摄像机内容。
这里是把功能增加,没有增加任何的实际效果代码。
这里我们需要设置窗口,需要获取当前的渲染逻辑和Shader
开方设置窗口
创建Pass,并且初始化队列和Shader
把Pass增加到渲染队列里
设置窗口
定义一个 Setting的
public class ColorTintRenderFeature: ScriptableRendererFeature
{
[System.Serializable]
public class Settings // 初始设置
{
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; // 设置渲染顺序
public Shader shader; // 设置后处理Shader
}
}
现在是设置完成,公开到界面上。
public Settings settings = new Settings(); // 开放设置 效果

定义Pass
定义一个Pass ,设置渲染Pass
ColorTintPass colorTintPass; // 设置渲染Pass
public override void Create() // 初始化 属性
{
this.name = &quot;ColorTintPass&quot;;
colorTintPass = new ColorTintPass(); // 初始化Pass
} 前定义一个Pass
在Create里初始化属性, 定义一个渲染显示的名字。
注意:这里目前是前把Pass定义好,还没有引入这里。
目前只是初始化,后面我们在AddRenderPasses里更新。
上面定义了渲染Pass,但是执行Pass的类还没有定义,所以报错。在文件下面,定义一个Pass
// 定义执行Pass
public class ColorTintPass : ScriptableRenderPass
{
} 扩展ScriptableRenderPass这个类是什么?
这个类是可以设置渲染队列的
这个类需要继承一个 Execute(执行)的方法函数
public class ColorTintPass : ScriptableRenderPass
{
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
}
} 注意:必须重载一个名为Execute的方法,该方法便是逻辑执行的地方。
定义需要的一些变量
static readonly string k_RenderTag = &quot;ColorTint Effects&quot;; // 设置渲染 Tags
static readonly int MainTexId = Shader.PropertyToID(&quot;_MainTex&quot;); // 设置主贴图
static readonly int TempTargetId = Shader.PropertyToID(&quot;_TempTargetColorTint&quot;); // 设置储存图像信息
ColorTint colorTint; // 传递到volume
Material colorTintMaterial; // 后处理使用材质
RenderTargetIdentifier currentTarget; // 设置当前渲染目标 定义构造函数
public ColorTintPass(RenderPassEvent evt, Shader ColorTintShader) // 输入渲染位置 Shader
{
renderPassEvent = evt; // 设置渲染事件的位置
var shader = ColorTintShader; // 输入Shader信息
} 判断一下Shader是否为空
public ColorTintPass(RenderPassEvent evt, Shader ColorTintShader) // 输入渲染位置 Shader
{
renderPassEvent = evt; // 设置渲染事件的位置
var shader = ColorTintShader; // 输入Shader信息
// 判断如果不存在Shader
if (shader = null) // Shader如果为空提示
{
Debug.LogError(&quot;没有指定Shader&quot;);
return;
}
//如果存在新建材质
colorTintMaterial = CoreUtils.CreateEngineMaterial(ColorTintShader);
} 定义一个Setup函数初始化
// 初始化函数
public void Setup(in RenderTargetIdentifier currentTarget)
{
this.currentTarget = currentTarget;
} 在Pass里的算结束了,我们回到RenderFeature模块。
Create( )函数
把ColorTintPass的 初始化Pass完成
上面已经制作

this.name = &quot;ColorTintPass&quot;; // 外部显示名字
colorTintPass = new ColorTintPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader); // 初始化Pass 效果

AddRenderPasses()函数
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) // Pass执行逻辑
{
colorTintPass.Setup(renderer.cameraColorTarget); // 初始化Pass里的属性
renderer.EnqueuePass(colorTintPass);
} 注意:如果出现错误检查是否导入空间导致的。

上面已经把初始化的内容传到渲染里了,我们下面开始写具体执行的内容
Execute() 函数
这里是Unity提供给我们的父类函数Execute的方法,该方法便是逻辑执行的地方。,这里我们重写Execute函数。
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 判断材质是否存在
if (colorTintMaterial == null)
{
Debug.LogError(&quot;材质初始化失败&quot;);
return;
}
// 摄像机是否开启后处理
if (!renderingData.cameraData.postProcessEnabled)
{
return;
}
// 渲染设置
var stack = VolumeManager.instance.stack; // 传入volume
colorTint = stack.GetComponent&lt;ColorTint&gt;(); // 拿到我们的volume
if (colorTint == null)
{
Debug.LogError(&quot; Volume组件获取失败 &quot;);
return;
}
} 判断材质是否为空

这里判断是否为什么空,如果为空,提示错误。
判断是否开启后处理

渲染设置,获取到我们的volume组件

扩展
上面我们可以看到定义函数有俩种方法一种是void,另一种是 override void
void 自定义函数方法。
override void 是父里已经有这个函数,使用(override void)这个重写这个函数。 这里还有一个一种使用方法
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
base.Execute(context,renderingData) // 调用父类的方法
} 使用 base 调用父类里的函数。
我们定义一个渲染函数 Render()
#region 渲染
void Render(CommandBuffer cmd, ref RenderingData renderingData)
{
ref var cameraData = ref renderingData.cameraData; // 获取摄像机属性
var camera = cameraData.camera; // 传入摄像机
var source = currentTarget; // 获取渲染图片
int destination = TempTargetId; // 渲染结果图片
colorTintMaterial.SetColor(&quot;_ColorTint&quot;, colorTint.ColorChange.value); // 获取value 组件的颜色
cmd.SetGlobalTexture(MainTexId, source); // 获取当前摄像机渲染的图片
cmd.GetTemporaryRT(destination, cameraData.camera.scaledPixelWidth, cameraData.camera.scaledPixelHeight, 0, FilterMode.Trilinear, RenderTextureFormat.Default);
cmd.Blit(source, destination); // 设置后处理
cmd.Blit(destination, source, colorTintMaterial, 0); // 传入颜色校正
}
#endregion 这里主要是获取场景渲染结果-在使用Shader进行调试输出。
扩展 #region #endregion 语法的使用方法。 可以把一个模块合并起来查看方便

渲染模块完成,在执行阶段调用渲染
Execute()函数完善
var cmd = CommandBufferPool.Get(k_RenderTag); // 设置渲染标签
Render(cmd, ref renderingData); // 设置渲染函数
context.ExecuteCommandBuffer(cmd); // 执行函数
CommandBufferPool.Release(cmd); // 释放 目前所有效果都完成,开始制作调色Shader
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class ColorTintRenderFeature: ScriptableRendererFeature
{
[System.Serializable]
public class Settings // 初始设置
{
public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; // 设置渲染顺序 在后处理前
public Shader shader; // 设置后处理Shader
}
public Settings settings = new Settings(); // 开放设置
ColorTintPass colorTintPass; // 设置渲染Pass
public override void Create() // 初始化 属性
{
this.name = &quot;ColorTintPass&quot;; // 外部显示名字
colorTintPass = new ColorTintPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader); // 初始化Pass
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) // Pass执行逻辑
{
colorTintPass.Setup(renderer.cameraColorTarget); // 初始化Pass里的属性
renderer.EnqueuePass(colorTintPass);
}
}
// 定义执行Pass
public class ColorTintPass : ScriptableRenderPass
{
static readonly string k_RenderTag = &quot;ColorTint Effects&quot;; // 设置渲染 Tags
static readonly int MainTexId = Shader.PropertyToID(&quot;_MainTex&quot;); // 设置主贴图
static readonly int TempTargetId = Shader.PropertyToID(&quot;_TempTargetColorTint&quot;); // 设置储存图像信息
ColorTint colorTint; // 传递到volume
Material colorTintMaterial; // 后处理使用材质
RenderTargetIdentifier currentTarget; // 设置当前渲染目标
#region 设置渲染事件
public ColorTintPass(RenderPassEvent evt, Shader ColorTintShader) // 输入渲染位置 Shader
{
renderPassEvent = evt; // 设置渲染事件的位置
var shader = ColorTintShader; // 输入Shader信息
// 判断如果不存在Shader
if (shader = null) // Shader如果为空提示
{
Debug.LogError(&quot;没有指定Shader&quot;);
return;
}
//如果存在新建材质
colorTintMaterial = CoreUtils.CreateEngineMaterial(ColorTintShader);
}
#endregion
// 初始化函数
#region 初始化
public void Setup(in RenderTargetIdentifier currentTarget)
{
this.currentTarget = currentTarget;
}
#endregion
#region 执行
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 判断材质是否为空
if (colorTintMaterial == null)
{
Debug.LogError(&quot;材质初始化失败&quot;);
return;
}
// 判断是否开启后处理
if (!renderingData.cameraData.postProcessEnabled)
{
return;
}
// 渲染设置
var stack = VolumeManager.instance.stack; // 传入volume
colorTint = stack.GetComponent&lt;ColorTint&gt;(); // 拿到我们的volume
if (colorTint == null)
{
Debug.LogError(&quot; Volume组件获取失败 &quot;);
return;
}
var cmd = CommandBufferPool.Get(k_RenderTag); // 设置渲染标签
Render(cmd, ref renderingData); // 设置渲染函数
context.ExecuteCommandBuffer(cmd); // 执行函数
CommandBufferPool.Release(cmd); // 释放
}
#endregion
#region 渲染
void Render(CommandBuffer cmd, ref RenderingData renderingData)
{
ref var cameraData = ref renderingData.cameraData; // 获取摄像机属性
var camera = cameraData.camera; // 传入摄像机
var source = currentTarget; // 获取渲染图片
int destination = TempTargetId; // 渲染结果图片
colorTintMaterial.SetColor(&quot;_ColorTint&quot;, colorTint.ColorChange.value); // 获取value 组件的颜色
cmd.SetGlobalTexture(MainTexId, source); // 获取当前摄像机渲染的图片
cmd.GetTemporaryRT(destination, cameraData.camera.scaledPixelWidth, cameraData.camera.scaledPixelHeight, 0, FilterMode.Trilinear, RenderTextureFormat.Default);
cmd.Blit(source, destination); // 设置后处理
cmd.Blit(destination, source, colorTintMaterial, 0); // 传入颜色校正
}
#endregion
} 注意:Shader里的属性和渲染调用里的变量名字需要一样。

sdader就是最简单的调整颜色Shader
Shader &quot;URP/ColorTint&quot;
{
Properties
{
_ColorTint(&quot;_ColorTint&quot;, Color) = (1,1,1,1)
_MainTex (&quot;Texture&quot;, 2D) = &quot;white&quot; {}
}
SubShader
{
Tags { &quot;RenderPipeline&quot;=&quot;UniversalPipeline&quot; }
Cull Off Zwrite Off ZTest Always
HLSLINCLUDE
#include &quot;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl&quot;
struct appdata
{
float4 positionOS : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
float4 _ColorTint;
CBUFFER_END
TEXTURE2D (_MainTex);
SAMPLER(sampler_MainTex);
ENDHLSL
Pass
{
Tags{ &quot;LightMode&quot;=&quot;UniversalForward&quot; }
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
v2f vert (appdata v)
{
v2f o;
o.vertex = TransformObjectToHClip(v.positionOS.xyz);
o.uv = v.texcoord;
return o;
}
half4 frag (v2f i) : SV_Target
{
half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.uv) * _ColorTint;
return col;
}
ENDHLSL
}
}
}
我们目前有3个文件,俩个脚本文件,一个Shader文件。

渲染组件里指定Shader,也可以设置渲染层级

在Volume组件里调整 颜色,就可以看到效果了,

效果

URP后处理是有4部分组成,分别是渲染器(Forward Renderer)— 后处理(Volume) — Pass模块 —Shader组成

脚本里主要模块
ScriptableRendererFeature 主要是有 设置模块(Settings) 初始化模块(Create) 执行逻辑模块(AddRenderPasses)

ScriptableRenderPass 有4部分 设置渲染事件(ColorTintPass) 初始化函数(Setup) 执行函数(Execute) 渲染函数(Render)
