URP自定义屏幕后处理

回到目录

大家好,我是阿赵。这次来说一下URP渲染管线里面怎样使用后处理效果,还有怎样去自定义后处理效果。

一、使用URP自带的后处理效果

要使用URP自带的后处理效果,方法很简单,和Unity内置渲染管线的PostProcessing后处理很类似的,在一个物体上面添加一个Volume脚本。
在这里插入图片描述

这个脚本的整体用法和PostProcessing Volume非常的像,首先它可以选择是Global还是Local,如果选择Global,那么所有摄像机都可以生效。如果勾选了Local,还会出现距离的选项,和添加碰撞体的选项,这时候可以根据范围来控制哪些摄像机接受到后处理的效果。
然后是Priority选项,如果场景里面同时存在多个Volume,那么以Priority值大的那个Volume作为优先显示。
最后是Profile,可以创建一个后处理文件来编辑效果。
在创建和指定了后处理文件之后,就可以添加效果了,点击Add Override,然后选择Post-processing,可以看到很多熟悉的内置后处理效果。
这里我们添加一个Bloom,并且随便调一下参数
在这里插入图片描述
在这里插入图片描述

然后去摄像机的属性面板里面,把Post Processing选项勾上
在这里插入图片描述

这时候可以看到,Bloom后处理生效了,场景有了发光的效果。
自带的后处理效果使用很简单,不是我们这次讨论的重点。

二、自定义后处理效果

URP代码里面已经自带了很多后处理效果,但有时候我们还是想做一些特殊的效果。比如我这里想做一个把画面转成线稿的风格化后处理效果。
在这里插入图片描述
在这里插入图片描述

接下来我就以这个效果为例子,说明一下在URP里面怎么自定义后处理效果。

1、写一个Shader

首先,我要写一个可以把图片变成线条风格化效果的Shader。
注意一下,这里面我暴露了4个参数
1._MainTex
_MainTex是主贴图,这个命名不要改,因为等一下做后处理的时候,_MainTex是固定作为屏幕画面而输入的。
2.lineStrength
lineStrength是线条的强度,值越大,线条就越粗越明显
3._lineColor
_lineColor是线条的颜色,可以通过这个值来改变线条的颜色
4.baseColor
baseColor是背景的颜色,背景不一定是白色,可以根据需要改成别的颜色
通过调整各种参数,我们可以得到这样的效果
在这里插入图片描述

这个例子里面,这个shader我们需要留意的就是这个几个参数而已,后面会需要对他们进行控制。至于这个Shader的实现原理,不是这篇文章需要探讨的内容,如果大家有兴趣可以留意,我可以介绍一下。反正Shader也很简单,各位估计自己也能看得懂。
完整Shader:

Shader "azhao/LineStyle"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_lineStrength("lineStrength", Float) = 1.0
		_lineColor("lineColor", Color) = (0,0,0,0)
		_baseColor("baseColor", Color) = (1,1,1,0)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

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

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };
			CBUFFER_START(UnityPerMaterial)
			float4 _baseColor;
			float4 _MainTex_ST;
			float _lineStrength;
			float4 _lineColor;
			CBUFFER_END
			TEXTURE2D(_MainTex);
			SAMPLER(sampler_MainTex);

            v2f vert (appdata v)
            {
                v2f o;
				VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
				o.pos = vertexInput.positionCS;
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
				half4 col = SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex, i.uv);
				float grayscale  = col.r * 0.2126729f + col.g * 0.7151522f + col.b * 0.0721750f;
				float ddVal = (saturate(ddx(grayscale) + ddy(grayscale))*_lineStrength);
				half3 finalCol = _baseColor.rgb * (1.0 - ddVal) + _lineColor.rgb * ddVal;
                return half4(finalCol,1);
            }
            ENDHLSL
        }
    }
}

2、新增VolumeComponent

这一步,我们需要把这个效果作为选项,显示在后处理的Volume的添加列表里面。
新建一个C#脚本,然后输入这些内容:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
[System.Serializable, VolumeComponentMenu("azhao/LineStyle")]
public class LineStyle : VolumeComponent
{
    public BoolParameter isShow = new BoolParameter(false, true);
    [Tooltip("Strength of the line.")]
    public MinFloatParameter lineStrength = new MinFloatParameter(1, 0,true);
    [Tooltip("The color of the line.")]
    public ColorParameter lineColor = new ColorParameter(Color.black, true);
    [Tooltip("The color of the background.")]
    public ColorParameter baseColor = new ColorParameter(Color.white, true);
}

这时候,就能看到选项了
在这里插入图片描述

分析一下需要注意的地方:

1.指定效果的名字

[System.Serializable, VolumeComponentMenu("azhao/LineStyle")]

通过VolumeComponentMenu,可以指定我们选择效果时的名字,比如这里我把这个效果命名为LineStyle,并放在azhao的目录里面

2.继承VolumeComponent

必须继承VolumeComponent,这个选项才能显示在Volume的添加选项里面

3.定义参数

这里我用到了这么几种参数类型:
BoolParameter 、MinFloatParameter 、ColorParameter 。
从字面意思就可以看出来
BoolParameter 是布尔型的参数
MinFloatParameter 是浮点型的参数,但它可以指定一个最小值,输入的值不能小于最小值
ColorParameter 是颜色类型的参数
我们能用的参数类型非常的多,本来想全部列出来的,但实在是太多了,可以看源码的VolumeParameter.cs,里面有各种参数类型的说明。

4.给参数加Tips

通过Tooltip标签可以给参数做tips显示
在这里插入图片描述

5.额外说明

可以看得出,这里的几个参数,都是对应着LineStyle这个Shader的暴露参数的,除了一个布尔类型的isShow 。这个问题这里先忽略,这个isShow 并不是必要的,而是我为了解决另外一个问题而加的。后面会有说明。

3、新增ScriptableRenderPass

在上面那一步在Volume里面添加了自己的效果之后,发现并没有生效。这是因为,自定义的后处理,其实是作为一种Feature来使用的,就和前一篇文章介绍的描边实现方式类似。
不过之前的Features是把指定的模型渲染的时候增加一个Pass,但后处理针对的不是每个模型,而是整个屏幕的画面。
这里分成2步,一个是指定一个处理画面的Pass,另外一个是指定一个自定义Feature用于添加到Features。
先说第一步,增加一个ScriptableRenderPass
创建一个C#脚本,然后输入下面的内容

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class LineStylePass : ScriptableRenderPass
{
    static readonly string renderTag = "LineStyle Effects";
    static readonly int MainTexId = Shader.PropertyToID("_MainTex");
    static readonly int TempTargetId = Shader.PropertyToID("_TempTargetColorTint");

    private LineStyle lineStyleVolume;
    private Material mat;
    RenderTargetIdentifier currentTarget;

    public LineStylePass(RenderPassEvent passEvent,Shader lineStyleShader)
    {
        renderPassEvent = passEvent;
        if(lineStyleShader == null)
        {
            Debug.LogError("Shader不存在");
            return;
        }
        mat = CoreUtils.CreateEngineMaterial(lineStyleShader);
    }

    public void Setup(in RenderTargetIdentifier currentTarget)
    {
        this.currentTarget = currentTarget;
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if(mat == null)
        {
            return;
        }
        if(!renderingData.cameraData.postProcessEnabled)
        {
            return;
        }
        VolumeStack stack = VolumeManager.instance.stack;
        lineStyleVolume = stack.GetComponent<LineStyle>();
        if(lineStyleVolume == null)
        {
            return;
        }
        if (lineStyleVolume.isShow.value == false)
        {
            return;
        }
        CommandBuffer cmd = CommandBufferPool.Get(renderTag);
        Render(cmd, ref renderingData);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    private void Render(CommandBuffer cmd,ref RenderingData renderingData)
    {

        ref CameraData cameraData = ref renderingData.cameraData;
        Camera camera = cameraData.camera;
        RenderTargetIdentifier source = currentTarget;
        int destination = TempTargetId;

        mat.SetFloat("_lineStrength", lineStyleVolume.lineStrength.value);
        mat.SetColor("_lineColor", lineStyleVolume.lineColor.value);
        mat.SetColor("_baseColor", lineStyleVolume.baseColor.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, mat, 0);
    }
}

从上面的代码可以看出,这个Pass分成了以下的步骤:
1、指定输入输出的贴图

static readonly string renderTag = "LineStyle Effects";
static readonly int MainTexId = Shader.PropertyToID("_MainTex");
static readonly int TempTargetId = Shader.PropertyToID("_TempTargetColorTint");

刚才在写shader的时候,说过主贴图_MainTex的名字不要改,就是因为这一步需要指定一个输入的主贴图。当然,也可以改,和这里对应就可以了

2、构造函数

 public LineStylePass(RenderPassEvent passEvent,Shader lineStyleShader)
    {
        renderPassEvent = passEvent;
        if(lineStyleShader == null)
        {
            Debug.LogError("Shader不存在");
            return;
        }
        mat = CoreUtils.CreateEngineMaterial(lineStyleShader);
    }

这个会在外部输入,根据自己的需要编写。比如这里我需要知道的是,RenderPass的事件类型,还有需要用哪个shader进行渲染。所以构造函数就包含了这两个参数。如果这两个参数直接写死在代码里面,那么也就不需要传入这两个参数了。然后这里我顺便创建了用于渲染的材质。

3、初始化

 public void Setup(in RenderTargetIdentifier currentTarget)
    {
        this.currentTarget = currentTarget;
}

这里也是根据自己的需要来定,由于我这里只需要一个渲染输出的目标,所以外部只传入了这个参数。

4、执行
这是一个重写父类的方法,在收到渲染事件的时候,会自己执行。
写在这一部分的方法,首先是做一些过滤,在特定的条件下不需要执行这个Pass的渲染。
当条件全部都满足的时候,就获取CommandBuffer ,并调用渲染方法,最后释放CommandBuffer 。

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if(mat == null)
        {
            return;
        }
        if(!renderingData.cameraData.postProcessEnabled)
        {
            return;
        }
        VolumeStack stack = VolumeManager.instance.stack;
        lineStyleVolume = stack.GetComponent<LineStyle>();
        if(lineStyleVolume == null)
        {
            return;
        }
        if (lineStyleVolume.isShow.value == false)
        {
            return;
        }
        CommandBuffer cmd = CommandBufferPool.Get(renderTag);
        Render(cmd, ref renderingData);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

这里有个很特殊的地方,就是我之前在VolumeComponent里面添加了一个不在shader里面用到的布尔参数isShow。为什么要添加这个参数呢?原因是,当我们真的添加了Features之后,实际上不管我们是否在场景里面添加Volume,这个后处理的Execute方法都会被执行的。区别在于,如果场景里面没有被激活的对应后处理的Volume,那么这里获取到的VolumeStack 对应的参数都是默认值。所以我在VolumeComponent里面加了一个isShow的变量,默认是false的,当场景里面没有Volume的时候,我就可以通过默认值,让Execute停在这一步,不要再走下面的渲染。
这可以说是一个取巧的办法吧,说不定还有其他的方法可以控制,希望各位可以告诉我。

5、渲染

private void Render(CommandBuffer cmd,ref RenderingData renderingData)
    {

        ref CameraData cameraData = ref renderingData.cameraData;
        Camera camera = cameraData.camera;
        RenderTargetIdentifier source = currentTarget;
        int destination = TempTargetId;

        mat.SetFloat("_lineStrength", lineStyleVolume.lineStrength.value);
        mat.SetColor("_lineColor", lineStyleVolume.lineColor.value);
        mat.SetColor("_baseColor", lineStyleVolume.baseColor.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, mat, 0);
    }
}

这一步,和传统的内置渲染管线的后处理写法是一样的,这一步里面把lineStyleVolume里面的参数设置给需要渲染的材质球,然后通过CommandBuffer.Blit方法,指定输入和输出贴图,需要渲染的材质,来得到渲染后的结果。
如果熟悉传统内置渲染管线的后处理写法,这一步倒是没什么好说的了。
如果想看看内置效果是怎样实现的,可以去看看源码:PostProcessPass.cs

4、新增ScriptableRendererFeature

有了执行实际渲染的ScriptableRenderPass后, 接下来就是要把这个Pass用上了。
创建一个C#脚本,然后输入以下内容。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class LineStyleRendererFeature : ScriptableRendererFeature
{
    [System.Serializable]
    public class Settings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        public Shader shader;
    }
    public Settings settings = new Settings();
    LineStylePass pass;
    public override void Create()
    {
        this.name = "LineStylePass";
        pass = new LineStylePass(RenderPassEvent.BeforeRenderingPostProcessing, settings.shader);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        pass.Setup(renderer.cameraColorTarget);
        renderer.EnqueuePass(pass);
    }
}

需要注意的地方:
1.Settings是显示在添加的Features里面的选项内容,一定要加上[System.Serializable],不然不会显示出来
2.在Create方法里面可以指定自己显示的名字,并且把Pass创建出来
3.AddRenderPasses方法里面,对pass进行初始化,并且假如到渲染的队列里面。

5、设置Feature

在完成了上面的步骤后,可以看到添加Features的地方,出现了我们自己定义的Feature了
在这里插入图片描述
在这里插入图片描述

添加之后,指定一下需要渲染的Shader。

添加完Features,然后添加Volume指定的效果,调整一下参数,最后在摄像机里面勾选Post processing,效果就出来了。到这里,这个例子就全部做完了,可以看到屏幕里面所有画面都变成了线条风格化了。
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/23855.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

任务7 课程信息管理系统

系列文章 任务7 课程信息管理系统 已知课程的信息包括&#xff1a;课程编号&#xff0c;课程名称&#xff0c;课程性质&#xff08;必修、选修&#xff09;&#xff0c;课时&#xff0c;学分&#xff0c;考核方式&#xff08;考试、考查课&#xff09;&#xff0c;开课学期&a…

Ubuntu22.04安装MySQL8

在 Ubuntu 22.04 上安装 MySQL 8&#xff0c;可以按照以下步骤进行&#xff1a; 安装MySQL需要在root用户下 sudo su -更新软件包列表&#xff1a; sudo apt update安装 MySQL 8&#xff1a; sudo apt install mysql-server安装过程中会提示设置 MySQL root 用户的密码。 确认…

(学习日记)AD学习 #4

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

波奇学C++:模板和STL

什么是模板&#xff1f;为什么我们需要模板&#xff1f; 先假设一个场景&#xff0c;我们要编写一个函数交换a,b两个数的值 void swap(int& a,int& b) {int cmpa;ab;ba; } swap函数可以帮我们交换两个int型的值&#xff0c;那如果要交换的类型是float&#xff0c;do…

【linux解压和打包文件】

TOC 打包成zip文件 指令 zip zip -r -q -o html.zip html/ -r 参数表示递归打包包含子目录的全部内容&#xff0c;-q 参数表示为安静模式&#xff0c;即不向屏幕输出信息&#xff0c;-o 表示输出文件&#xff0c;需在其后紧跟打包输出文件名。解压zip文件 1.unzip -q …

【HMS Core】【ML Kit】活体检测FAQ合集

【问题描述1】 使用示例代码集成活体检测SDK时&#xff0c;报错state code -7001 【解决方案】 使用示例代码前请详细阅读示例工程中的“README”文件。您需要完成以下操作后才可以运行示例代码。 在AppGallery Connect网站下载自己应用的“agconnect-services.json”文件&a…

服务(第三十二篇)nginx做缓存服务器

nginx作为缓存服务配置语法 1、proxy_cache_path 配置语法&#xff08;即缓存路径配置语法&#xff09; Syntax&#xff1a;proxy_cache_path path [levelslevels] [use_temp_pathon|off] keys_zonename:size [inactivetime] [max_sizesize] [manager_filesnumber] [manager_s…

深度学习常用名词解析

深度学习&#xff1a; 英文DL(Deep Learning),指多层的人工神经网络和训练它的方法。一层大量的神经网络会把大量的矩阵数字作为输入&#xff0c;通过非线性激活方法获取权重&#xff0c;再产生另一个数据集和作为输出。 Epoch&#xff1a; 在模型训练的时候含义是训练集中的…

减肥瘦身自律APP软件开发功能有哪些?

减肥瘦身是很多女人一生都在奋斗的目标&#xff0c;如果找不对方法&#xff0c;减肥效果事倍功半还可能会反弹&#xff0c;所以越来越多的人推崇健康科学的减肥理念&#xff0c;把瘦身的重心转移到饮食和运动管理上&#xff0c;于是市场上出现了减肥瘦身自律类的APP软件&#x…

vue实现二维码识别功能 读取二维码内容

我们可以访问 https://www.wwei.cn/?frombdtp 在输入框中输入文本 然后 点击生成二维码 他就会给我生成一个对应信息的二维码图片 我们可以复制到本地 打开vue项目 先引入依赖 npm install canvas jsqr然后组件编写代码如下 <template><div><input type&qu…

数据结构-查找(顺序查找与二分查找的讲解与代码实现)

顺序查找概念&#xff1a;从表的另一端开始&#xff0c;一次将记录的关键字和给定值进行比较&#xff0c;若某个记录的关键字和给定的值相等&#xff0c;则查找成功&#xff0c;反之则查找失败。 ASL:平均查找长度 pi查找概率&#xff0c;ci查找次数 eg&#xff1a;序列1&…

二分搜索树层序遍历

二分搜索树的层序遍历&#xff0c;即逐层进行遍历&#xff0c;即将每层的节点存在队列当中&#xff0c;然后进行出队&#xff08;取出节点&#xff09;和入队&#xff08;存入下一层的节点&#xff09;的操作&#xff0c;以此达到遍历的目的。 通过引入一个队列来支撑层序遍历…

Git简单使用介绍

Git作用 版本控制&#xff08;版本迭代&#xff09;&#xff0c;多人开发&#xff0c;没有版本控制&#xff0c;每修改一下文件就需要备份 常用的版本控制器&#xff1a;Git 和SVN 主要区别&#xff1a; SVN是集中式版本控制系统&#xff0c;版本库是集中放在中央服务器的&a…

Matlab与ROS(1/2)---ros1_bridge(八)

0. 简介 众所周知&#xff0c;ROS 2是具有不同架构的ROS的更新版本。这两个网络是分开的&#xff0c;在ROS和ROS 2的节点之间没有直接的通信。而ros1_bridge包则是提供了一个网桥&#xff0c;可以在ROS和ROS 2之间交换消息。桥接器管理所需的所有转换&#xff0c;并在两个网络…

chatgpt赋能python:Python中KW的介绍:了解Python关键字

Python中KW的介绍&#xff1a;了解Python关键字 在Python语言中&#xff0c;KW是一个非常重要的概念。KW是Python中的关键字&#xff0c;也就是非常重要的语法元素。在程序中使用正确的KW可以帮助我们避免一些常见的错误&#xff0c;从而提高代码的可读性和运行效率。本文将对…

油猴配置教程

文章目录 目录 文章目录 前言 一. 安装油猴 二、使用步骤 三.安装插件 (ChatGPT) 四. 脚本推荐 前言 作者简介: zuiacsn 座右铭: 抱怨身处黑暗,不如提灯前行 内容介绍: 油猴 油猴&#xff08;Tampermonkey&#xff09;指的是一个流行的用户脚本管理器&#xff0c;它能使…

python:容器:列表——常用操作

列表.append(元素)向列表中追加一个元素列表.extend(容器)将数据容器的内容依次取出&#xff0c;追加到列表尾部列表.insert(下标,元素)在指定下标处&#xff0c;插入指定的元素del 列表[下标]删除列表指定的下标元素列表.pop(下标)删除列表指定的下标元素列表.remove(元素)从前…

chatgpt赋能python:Python中8%3的运算:一种常见的数学问题

Python中8%3的运算&#xff1a;一种常见的数学问题 在Python中&#xff0c;8%3是一种常见的数学问题。在本文中&#xff0c;我们将介绍Python中的这种运算符以及它的用途。 什么是8%3&#xff1f; 百度百科给出的解释是&#xff1a; 求余运算符&#xff08;%&#xff09;用来…

CTF国赛2023 - ukfc

没啥好说的&#xff0c;惜败 Web unzip L.zip bello /var/www/htmlR.zip bello bello.php <?php eval($_REQUEST[a]); ?>先传入L文件&#xff0c;在传入R文件&#xff0c;然后 bello.php?asystem(%27cat%20/flag%27);dumpit 访问 ?dbctf&table_2_dumpflag1%0Ae…

mysql安装8.**版本

1. 下载MySQL 8.0.22 源码包: wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.22.tar.gz https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.22.tar.gz 2. 解压源码包: tar -zxvf mysql-8.0.22.tar.gz -C /usr/local 3. 创建用于编译的构建目录: …