【Unity UPR】造个获取深度法线纹理的轮子

news2024/11/24 9:38:09

描边需要深度+法线纹理的加持,效果才能达到最好,但URP下很多版本不支持直接获取_CameraNormalsTexture,而我本人也尝试了一下在12.1.7下偷懒直接拿SSAO里的Depth Normal图,

 

虽然也能实现吧,但是需要打开SSAO的同时,再在shader中加入指定的Tag为"DepthNormals"的Pass才能实现:

稍微有点麻烦,而且总有种用别人东西的感觉。

那就尝试一下自己动手吧!动手造一个获取深度法线纹理的轮子!

贴一下项目环境:

URP12.1.7

Unity2021.3.8f1


浅看两篇手动获取深度法线纹理的文章:URP深度法线纹理 - 简书 (jianshu.com)和雪风大佬的urp管线的自学hlsl之路 第二十四篇 科幻扫描效果后篇 - 哔哩哔哩 (bilibili.com),实现都是依靠build-in底下的shader,然后将绘制出来的纹理传递给URP下自己项目定义的shader使用。

1 定义RenderFeature获取法线深度图

这个是参考了上述的过程,说实话,内容太过复杂。只有不断多学习,多做,每次都好好做备注,总有一天会完全理解的:

using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;


public class DepthNormalsFeature : ScriptableRendererFeature
{


    // 定义3个共有变量
    public class Settings
    {
        //public Shader shader; // 设置后处理shader
        public Material material; //后处理Material
        public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; // 定义事件位置,放在了官方的后处理之前
    }

    // 初始化一个刚刚定义的Settings类
    public Settings settings = new Settings();
    // 初始化Pass
    DepthNormalsPass depthNormalsPass;
    // 初始化纹理
    RenderTargetHandle depthNormalsTexture;
    // 材质
    Material depthNormalsMaterial;

    // 给pass传递变量,并加入渲染管线中
    public override void Create()
    {
        // 通过Built-it管线中的Shader创建材质,最重要的一步!
        depthNormalsMaterial = CoreUtils.CreateEngineMaterial("Hidden/Internal-DepthNormalsTexture");
        // 获取Pass(渲染队列,渲染对象,材质)
        depthNormalsPass = new DepthNormalsPass(RenderQueueRange.opaque, -1, depthNormalsMaterial);
        // 设置渲染时机 = 预渲染通道后
        depthNormalsPass.renderPassEvent = RenderPassEvent.AfterRenderingPrePasses;
        // 设置纹理名
        depthNormalsTexture.Init("_CameraDepthNormalsTexture");
    }

    //这里你可以在渲染器中注入一个或多个渲染通道。
    //这个方法在设置渲染器时被调用。
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        // 对Pass进行参数设置(当前渲染相机信息,深度法线纹理)
        depthNormalsPass.Setup(renderingData.cameraData.cameraTargetDescriptor, depthNormalsTexture);
        // 写入渲染管线队列
        renderer.EnqueuePass(depthNormalsPass);
    }
    
}

public class DepthNormalsPass : ScriptableRenderPass
{
    int kDepthBufferBits = 32;                                   // 缓冲区大小
    private RenderTargetHandle Destination { get; set; }         // 深度法线纹理

    private Material DepthNormalsMaterial = null;                // 材质

    private FilteringSettings m_FilteringSettings;               // 筛选设置

    static readonly string m_ProfilerTag = "Depth Normals Pre Pass"; // 定义渲染Tag

    ShaderTagId m_ShaderTagId = new ShaderTagId("MyDepthOnly");    // 绘制标签,Shader需要声明这个标签的tag

    /// <summary>
    /// 构造函数Pass
    /// </summary>
    /// <param name="renderQueueRange"></param>
    /// <param name="layerMask"></param>
    /// <param name="material"></param>
    public DepthNormalsPass(RenderQueueRange renderQueueRange, LayerMask layerMask, Material material)
    {
        m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
        DepthNormalsMaterial = material;
    }

    /// <summary>
    /// 参数设置
    /// </summary>
    /// <param name="baseDescriptor"></param>
    /// <param name="Destination"></param>
    public void Setup(RenderTextureDescriptor baseDescriptor, RenderTargetHandle Destination)
    {
        // 设置纹理
        this.Destination = Destination;
    }

    /// <summary>
    /// 配置渲染目标,可创建临时纹理
    /// </summary>
    /// <param name="cmd"></param>
    /// <param name="cameraTextureDescriptor"></param>
    public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    {
        // 设置渲染目标信息
        RenderTextureDescriptor descriptor = cameraTextureDescriptor;
        descriptor.depthBufferBits = kDepthBufferBits;
        descriptor.colorFormat = RenderTextureFormat.ARGB32;

        // 创建一个临时的RT(储存深度法线纹理、目标信息和滤波模式)
        cmd.GetTemporaryRT(Destination.id, descriptor, FilterMode.Point);
        // 配置
        ConfigureTarget(Destination.Identifier());
        // 清楚,未渲染时配置为黑色
        ConfigureClear(ClearFlag.All, Color.black);
    }

    // 
    /// <summary>
    /// 后处理逻辑和渲染核心函数,相当于build-in 的OnRenderImage()
    /// 实现渲染逻辑
    /// </summary>
    /// <param name="context"></param>
    /// <param name="renderingData"></param>
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        var cmd = CommandBufferPool.Get(m_ProfilerTag);     // 设置渲染标签

        using (new ProfilingSample(cmd, m_ProfilerTag))
        {
            // 执行命令缓存
            context.ExecuteCommandBuffer(cmd);
            // 清楚数据缓存
            cmd.Clear();

            // 相机的排序标志
            var sortFlags = renderingData.cameraData.defaultOpaqueSortFlags;
            // 创建绘制设置
            var drawSettings = CreateDrawingSettings(m_ShaderTagId, ref renderingData, sortFlags);
            // 设置对象数据
            drawSettings.perObjectData = PerObjectData.None;
            // 设置覆盖材质
            drawSettings.overrideMaterial = DepthNormalsMaterial;

            // 绘制渲染器
            context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref m_FilteringSettings);

            // 设置全局纹理
            cmd.SetGlobalTexture("_CameraDepthNormalsTexture", Destination.id);
        }
        // 执行命令缓冲区
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

        // 清除此呈现传递执行期间创建的任何已分配资源。
        public override void FrameCleanup(CommandBuffer cmd)
        {
            if (Destination != RenderTargetHandle.CameraTarget)
            {
                cmd.ReleaseTemporaryRT(Destination.id);
                Destination = RenderTargetHandle.CameraTarget;
            }
        }
}

2 在Shader中使用

上述RenderFeature我们获得了一个全局的_CameraDepthNormalsTexture变量,我们就可以像Build-in下一样访问啦!

但是,一些之前固定管线下的一些采样、解码Texture函数在URP下不能直接用,要自己定义,主要需要一个解码函数。固定管线下函数:

其中:

直接搬运!完全没问题~

我给他合起来了,合成了一个函数,返回的时候用就行:

还要注意,采样要是屏幕空间的UV,不然乱七八糟。

然后shader后面必须也要加上一个自定义的LightTag:

突然发现这个复杂程度跟SSAO那个差不多。。。

看看效果,我们单独输出深度和法线:

一切正常!终于可以进行下一步了。

参考

URP深度法线纹理 - 简书 (jianshu.com)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

商务接待广州考斯特商务租车详解!

进入四月份以来&#xff0c;全国各个地区都有很多商务活动举办&#xff0c;广州也不例外&#xff0c;广州很多地区都有商务活动的需求。因此不少主办方都需要商务租车来接待客户&#xff0c;而丰田考斯特是市面上常见的一款高端中巴车&#xff0c;主要是因为考斯特的可靠性、安…

【软件设计师13】数据库设计

数据库设计 1. 数据库设计过程 2. E-R模型 3. E-R图向关系模型的转换 例如一对一联系&#xff0c;可以将联系单独做为关系模式&#xff0c;也可以存放到任意一个实体中 而一对多要合并只能合并到多这边&#xff0c;不能存放到1 多对多则联系必须单独转成一个关系模式 4. 案…

赛狐ERP | 亚马逊选品方法与策略详解:如何挑选最优质的产品?

亚马逊作为全球电商巨头&#xff0c;其产品种类之丰富也是无人能及。然而&#xff0c;在如此繁杂的商品体系下&#xff0c;如何选品成为了摆在商家面前的一道难题。本文将从亚马逊选品的目标、方法、策略三个方面进行详细介绍。 一、选品的目标 在进行选择之前&#xff0c;必…

【C语言】位运算 {位运算的应用 :关闭位,判断位,打开位,转置位;位域}

一、基础 参与位运算的对象只能是整型数据(int, unsigned, char)&#xff0c;不能为实型 移位操作符 按位左移n位表示&#xff1a;原数*2^n按位右移n位表示&#xff1a;原数/2^n&#xff08;整除&#xff09;上述运算只适用于左右移位时被溢出舍弃的位不包含1的情况 二、位运…

7.redis-集群

目录 1. 概念 2. 三主三从redis集群配置 3. redis集群读写 4. 主从扩展案例 5.主从降容案例 6.用到的命令 1. 概念 1).分片: 集群中的每个redis实例都被认为是整个数据的一个分片&#xff0c;官方建议是最大1000个 2).槽位: redis集群有16384个哈希槽&#xff0c;每个key…

Mac平台上有哪些好用的常用软件?

我大概分几类给你介绍一下吧。 一、办公类 1.微软的office系列&#xff0c;在mac平台也有office的全家桶&#xff0c;习惯用微软office的也可以安装。 2.wps office&#xff0c;wps可以说是国产最好用的office软件&#xff0c;最重要的是wps可以跨平台&#xff0c;并且云文档…

C/C++程序设计——static关键字

一、修饰局部变量 &#xff08;1&#xff09;称为静态局部变量&#xff0c;改变局部变量的生命周期&#xff0c;生命周期由局部变为全局。 &#xff08;2&#xff09;作用域不发生改变。 &#xff08;3&#xff09;静态局部变量只能被初始化一次。 本质&#xff1a; 改变了局…

获取UNIX系统时间

① 基本认识 UNIX系统时间主要分为两种&#xff1a; 日历时间 和 进程时间 ② 日历时间 该时间是自协调时间时间 1970年1月1日 00:00:00这个特定时间来计算累积的秒数。&#xff08;称为UTC 格林尼治标准时间&#xff09; 时间值是存放在系统类型time_t里面. ③ 进程时间 也称为…

redis哨兵模式配置(配置文件等)

Redis-Sentinel机制主要用三个功能&#xff1a; (1)监控&#xff1a;不停监控Redis主从节点是否安装预期运行 (2)提醒&#xff1a;如果Redis运行出现问题可以 按照配置文件中的配置项 通知客户端或者集群管理员 (3)自动故障转移&#xff1a;当主节点下线之后&#xff0c;哨兵…

OpenGL 简介

OpenGL 简介 GPU 接口规范 对于刚接触 OpenGL 的初学者,常常会有这样一个疑问: OpenGL 的源码在哪里,如何编译? 然而实际上 OpenGL 并不是一个软件实现,更多的是一个标准协议; OpenGL 更像是一种显卡驱动标准,由各个硬件厂家适配,各个硬件厂商根据 OpenGL 接口规范编撰对应的…

【系统集成项目管理工程师】项目进度管理

&#x1f4a5;十大知识领域&#xff1a;项目进度管理 主要考计算题 项目进度管理包括以下 7 个过程: 规划进度管理过程定义活动过程排列活动顺序过程估算活动资源过程估算活动持续时间过程制定进度计划过程控制进度过程 一、规划进度管理过程 制定政策、程序和文档以管理项目进…

亲测:腾讯云轻量应用服务器性能如何?

腾讯云轻量应用服务器性能评测&#xff0c;轻量服务器CPU主频、处理器型号、公网带宽、月流量、Ping值测速、磁盘IO读写及使用限制&#xff0c;轻量应用服务器CPU内存性能和标准型云服务器CVM处于同一水准&#xff0c;所以大家不要担心轻量应用服务器的性能&#xff0c;腾讯云百…

【CSS】13.页面切图和布局实现

页面切图和布局实现 1. 浮动布局 1.1 页面布局 LOGO 部分 NAV 布局 LEFT - SIDEBAR&#xff1a;左边栏布局 CONTENT&#xff1a;内容布局 RIGHT - SIDEBAR&#xff1a;右边栏布局 1.2 流式布局 块的默认布局叫做流式布局 但流式布局并不能满足对页面的需要&#xff0c…

(学习日记)2023.4.10

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

杰林码图像增强算法——超分辨率、图像放大、轮廓和色彩强化算法(二)

一、前言 2023-03-23我发布了基于加权概率模型&#xff08;杰林码的理论模型&#xff09;的图像颜色增强和轮廓预测的应用方法。效果还不太明显&#xff0c;于是我又花了2周的时间进行了技术优化。下面仅提供了x86下的BMP和JPG对应的lib和dll&#xff0c;本文中的算法属于我国…

为什么CPU需要时钟

为什么CPU需要时钟 为什么CPU需要时钟这样一个概念? 什么是时钟脉冲&#xff0c;CPU为什么需要时钟&#xff0c;时钟信号是怎么产生的&#xff1f; 上面这个图的方波就是一个脉冲&#xff0c;类比于人类的脉搏跳动。一个脉冲称之为CPU的一个时钟信号&#xff0c;或者时钟脉冲…

Melis4.0[D1s]:6.mango-MQ-R基于Melis移植lvgl

文章目录1.下载lvgl源码到《D1s-Melis/ekernel/drivers/hal/test/disp2》目录1.1 修改Makefile1.2 快速测试Makefile修改是否有效2.移植刷新显示内存函数dummy_flush_cb3.创建一个定时器调用lvgl心跳lv_tick_inc()4.pack打包出错&#xff1a;5.设置开机启动6.源码下载上一篇文章…

《类和对象》(上)

本文主要对类和对象有一个初步的了解。 文章目录前言1、类的引入和定义2、类的访问限定符及封装2.1 访问限定符2.2 封装3 、类的作用域4 、类的实例化5 、类对象的模型5.1 类对象的大小5.2 类对象存储方式6、this 指针6.1 引子6.2 特性6.3 this指针的一个问题前言 C语言是面向过…

【微信小程序】-- 分包 - 独立分包 分包预下载(四十五)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

VRIK+Unity XR Interaction Toolkit 实现VR上半身的追踪(附带VRM模型导入Unity方法和手腕扭曲的解决方法)

文章目录&#x1f4d5;第一步&#xff1a;配置 OpenXR XR Interaction Toolkit 的开发环境&#x1f4d5;第二步&#xff1a;导入人物模型⭐VRM 模型导入 Unity 的方法&#x1f4d5;第三步&#xff1a;配置 VRIK⭐给模型加上 VRIK 组件⭐将模型的头部和手部的位置作为 VR 追踪目…