URP | 后处理-自定义后处理
那个人真狗
2022年07月28日 18:18
收录于文集
共43篇

URP逻辑

主要是有3个模块组成

  • Volume 模块这个就是后处理组件模块

  • RenderFeature渲染器模块

  • Pass模块是编辑后处理核心代码类。

URP管线设置后处理

<aside> 💡 创建渲染管线

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

</aside>

  • 渲染器

现在项目中没有自定义后处理效果,创建一个。

创建Volume 模块

我们调整后处理是在Volume组件调整,所以我们需要定义一个脚本把参数穿过来。

  • 新建一个脚本,命名ColorTint 继承 VolumeComponent类

代码块
JavaScript
自动换行
复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;



public class ColorTint : VolumeComponent
{
    // 设置颜色参数
    public ColorParameter ColorChange = new ColorParameter(Color.white, true);  
}
复制成功
  • 注意:导入的命名空间是需要注意的。

  • 效果

创建RenderFeature模块

RenderFeature模块 是用来设置Shader以及渲染大体逻辑的,

首先,新建一个继承ScriptableRendererFeature的脚本,

ScriptableRendererFeature是URP提供给用户用来扩展自定义渲染功能的入口

代码块
JavaScript
自动换行
复制代码
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的

代码块
JavaScript
自动换行
复制代码
public class ColorTintRenderFeature: ScriptableRendererFeature
{
    [System.Serializable]

    public class Settings       // 初始设置
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;        // 设置渲染顺序
        public Shader shader;      // 设置后处理Shader
    }
}
复制成功
  • 现在是设置完成,公开到界面上。

代码块
JavaScript
自动换行
复制代码
public Settings settings = new Settings();            // 开放设置
复制成功
  • 效果

定义Pass

  • 定义一个Pass ,设置渲染Pass

代码块
JavaScript
自动换行
复制代码
ColorTintPass colorTintPass;    // 设置渲染Pass

    public override void Create() // 初始化 属性
    {
        this.name = "ColorTintPass";
        colorTintPass = new ColorTintPass();      // 初始化Pass
    }
复制成功

前定义一个Pass

在Create里初始化属性, 定义一个渲染显示的名字。

注意:这里目前是前把Pass定义好,还没有引入这里。

目前只是初始化,后面我们在AddRenderPasses里更新。

执行PASS模块

  • 上面定义了渲染Pass,但是执行Pass的类还没有定义,所以报错。在文件下面,定义一个Pass

代码块
JavaScript
自动换行
复制代码
// 定义执行Pass
public class ColorTintPass : ScriptableRenderPass
{

}
复制成功

扩展ScriptableRenderPass这个类是什么?

这个类是可以设置渲染队列的

  • 这个类需要继承一个 Execute(执行)的方法函数

代码块
JavaScript
自动换行
复制代码
public class ColorTintPass : ScriptableRenderPass
{
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {

    }
}
复制成功

注意:必须重载一个名为Execute的方法,该方法便是逻辑执行的地方。

  • 定义需要的一些变量

代码块
JavaScript
自动换行
复制代码
		static readonly string k_RenderTag = "ColorTint Effects";          // 设置渲染 Tags
    static readonly int MainTexId = Shader.PropertyToID("_MainTex");   // 设置主贴图
    static readonly int TempTargetId = Shader.PropertyToID("_TempTargetColorTint");    // 设置储存图像信息

    ColorTint colorTint;           // 传递到volume
    Material colorTintMaterial;     // 后处理使用材质

    RenderTargetIdentifier currentTarget;   // 设置当前渲染目标
复制成功
  • 定义构造函数

代码块
JavaScript
自动换行
复制代码
public ColorTintPass(RenderPassEvent evt, Shader ColorTintShader)        // 输入渲染位置    Shader
    {
        renderPassEvent = evt;         // 设置渲染事件的位置
        var shader = ColorTintShader;  // 输入Shader信息
    }
复制成功

判断一下Shader是否为空

代码块
JavaScript
自动换行
复制代码
public ColorTintPass(RenderPassEvent evt, Shader ColorTintShader)        // 输入渲染位置    Shader
    {
        renderPassEvent = evt;         // 设置渲染事件的位置
        var shader = ColorTintShader;  // 输入Shader信息
        // 判断如果不存在Shader
        if (shader = null)         // Shader如果为空提示
        {
            Debug.LogError("没有指定Shader");
            return;
        }
        //如果存在新建材质
        colorTintMaterial = CoreUtils.CreateEngineMaterial(ColorTintShader);
    }
复制成功
  • 定义一个Setup函数初始化

代码块
JavaScript
自动换行
复制代码
// 初始化函数
    public void Setup(in RenderTargetIdentifier currentTarget)
    {
        this.currentTarget = currentTarget;
    }
复制成功

在Pass里的算结束了,我们回到RenderFeature模块。

Create( )函数

把ColorTintPass的 初始化Pass完成

  • 上面已经制作

代码块
JavaScript
自动换行
复制代码
this.name = "ColorTintPass";                    // 外部显示名字
colorTintPass = new ColorTintPass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader);      // 初始化Pass
复制成功
  • 效果

AddRenderPasses()函数

代码块
JavaScript
自动换行
复制代码
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) // Pass执行逻辑
    {
        colorTintPass.Setup(renderer.cameraColorTarget);                 // 初始化Pass里的属性
        renderer.EnqueuePass(colorTintPass);
    }
复制成功
  • 注意:如果出现错误检查是否导入空间导致的。

  • 上面已经把初始化的内容传到渲染里了,我们下面开始写具体执行的内容

Execute() 函数

这里是Unity提供给我们的父类函数Execute的方法,该方法便是逻辑执行的地方。,这里我们重写Execute函数。

代码块
JavaScript
自动换行
复制代码
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        // 判断材质是否存在
        if (colorTintMaterial == null)
        {
            Debug.LogError("材质初始化失败");
            return;
        }
        // 摄像机是否开启后处理
        if (!renderingData.cameraData.postProcessEnabled)
        {
            return;
        }
        // 渲染设置
        var stack = VolumeManager.instance.stack;          // 传入volume
        colorTint = stack.GetComponent<ColorTint>();       // 拿到我们的volume
        if (colorTint == null)
        {
            Debug.LogError(" Volume组件获取失败 ");
            return;
        }
    }
复制成功
  • 判断材质是否为空

这里判断是否为什么空,如果为空,提示错误。

  • 判断是否开启后处理

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

扩展

上面我们可以看到定义函数有俩种方法一种是void,另一种是 override void

void 自定义函数方法。

override void   是父里已经有这个函数,使用(override void)这个重写这个函数。 这里还有一个一种使用方法

代码块
JavaScript
自动换行
复制代码
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) 
    { 
        base.Execute(context,renderingData)  // 调用父类的方法

    }
复制成功

使用 base 调用父类里的函数。

计算渲染

我们定义一个渲染函数 Render()

代码块
JavaScript
自动换行
复制代码
#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("_ColorTint", 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()函数完善

代码块
JavaScript
自动换行
复制代码
var cmd = CommandBufferPool.Get(k_RenderTag);   // 设置渲染标签 
Render(cmd, ref renderingData);                 // 设置渲染函数
context.ExecuteCommandBuffer(cmd);              // 执行函数
CommandBufferPool.Release(cmd);                 // 释放
复制成功

目前所有效果都完成,开始制作调色Shader

代码块
JavaScript
自动换行
复制代码
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 = "ColorTintPass";        // 外部显示名字
        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 = "ColorTint Effects";          // 设置渲染 Tags
    static readonly int MainTexId = Shader.PropertyToID("_MainTex");   // 设置主贴图
    static readonly int TempTargetId = Shader.PropertyToID("_TempTargetColorTint");    // 设置储存图像信息

    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("没有指定Shader");
            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("材质初始化失败");
            return;
        }
        // 判断是否开启后处理
        if (!renderingData.cameraData.postProcessEnabled)
        {
            return;
        }
        // 渲染设置
        var stack = VolumeManager.instance.stack;          // 传入volume
        colorTint = stack.GetComponent<ColorTint>();       // 拿到我们的volume
        if (colorTint == null)
        {
            Debug.LogError(" Volume组件获取失败 ");
            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("_ColorTint", 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里的属性和渲染调用里的变量名字需要一样。

Shader部分

  • sdader就是最简单的调整颜色Shader

代码块
JavaScript
自动换行
复制代码
Shader "URP/ColorTint"
{
    Properties
    {
        _ColorTint("_ColorTint", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderPipeline"="UniversalPipeline" }
        Cull Off Zwrite Off ZTest Always
        HLSLINCLUDE

        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            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{ "LightMode"="UniversalForward" }

            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)