【Unity Shader入门精要 第13章】使用深度和法线纹理(二)

news2024/11/27 16:32:10

1. 再谈运动模糊

之前的文章中曾经通过保存渲染结果进行叠加的方式实现过运动模糊效果,下面的例子我们通过深度纹理重建世界坐标的方式来实现运动模糊:

  • 首先,基于深度纹理重建像素的世界坐标,原理在【Unity Shader入门精要 第13章】使用深度和法线纹理(一)中介绍过
  • 然后使用保存的前一帧的VP矩阵进行矩阵变换,求出前一帧的NDC坐标
  • 通过两帧的NDC坐标计算像素的速度
  • 最后通过速度进行模糊处理

由于需要对摄像机进行操作,为了方便拿到当前摄像机,在后处理父类中加入如下代码:

private Camera mCamera;
public Camera Camera
{
	get
    {
    	if (null == mCamera) mCamera = gameObject.GetComponent<Camera>();
        return mCamera;
    }
}

另外,在进行运动模糊的后处理子类脚本中,还需要设置摄像机的深度纹理模式:

private void OnEnable()
{
    Camera.depthTextureMode |= DepthTextureMode.Depth;
}

测试脚本

using UnityEngine;

public class PostEffect_MotionBlur_NDC : PostEffectBase
{
    public Shader MotionBlurShader_NDC;
    public Material MotionBlurMat_NDC;

    [Range(0, 1)]
    public float BlurSize;
    [Range(1, 4)]
    public int BlurRound;

    private Matrix4x4 mPreviousVP;

    private void OnEnable()
    {
        Camera.depthTextureMode |= DepthTextureMode.Depth;
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Material _mat = CheckShaderAndMaterial(MotionBlurShader_NDC, MotionBlurMat_NDC);
        if(null == _mat) Graphics.Blit(src, dest);
        else
        {
            _mat.SetFloat("_BlurRound", BlurRound);
            _mat.SetFloat("_BlurSize", BlurSize);
            _mat.SetMatrix("_PreviousVP", mPreviousVP);
            Matrix4x4 _curVP = Camera.projectionMatrix * Camera.worldToCameraMatrix;
            _mat.SetMatrix("_InversVP", _curVP.inverse);
            mPreviousVP = _curVP;

            Graphics.Blit(src, dest, _mat);
        }
    }
}

测试Shader:

Shader "MyShader/Chapter_13/Chapter_13_MotionBlur_NDC_Shader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        ZTest Always ZWrite Off Cull Off
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            sampler2D _CameraDepthTexture;
            float4x4 _InversVP;
            float4x4 _PreviousVP;
            half _BlurRound;
            half _BlurSize;
            
            v2f vert(appdata_img v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv.xy = v.texcoord;
                o.uv.zw = v.texcoord;
                #if UNITY_UV_STARTS_AT_TOP 
                    if (_MainTex_TexelSize.y < 0)
                        o.uv.w = 1 - o.uv.w;
                #endif
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed _d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.zw);
                fixed4 _curNDC = fixed4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, _d * 2 - 1, 1);
                float4 _worldPos = mul(_InversVP, _curNDC);
                _worldPos /= _worldPos.w;
                float4 _preNDC = mul(_PreviousVP, _worldPos);
                _preNDC /= _preNDC.w;
                float2 _velocity = (_curNDC.xy - _preNDC.xy) * 0.5;
                
                float2 _uv = i.uv.xy;
                float4 _color = tex2D(_MainTex, i.uv);
                for(int it = 1; it < _BlurRound; it++)
                {
                    _uv += _velocity * _BlurSize;
                    _color += tex2D(_MainTex, _uv);
                }
                _color /= _BlurRound;
                
                return fixed4(_color.rgb, 1);
            }
        
            ENDCG
        }
        
    }
}

测试效果:
在这里插入图片描述

2. 全局雾效

2.1 Unity内置雾效

Unity内置的雾效可以产生基于距离的线性或指数的雾效,如果在自己编写的Shader中支持雾效,需要在Shader中添加#pragma multi_compile_fog编译指令,同时,在Shader中还要通过 UNITY_FOG_COORDS、UNITY_TRANSFER_FOG、UNITY_APPLY_FOG 宏计算雾效效果(过程与阴影一致,就不演示了)。

这种雾效的缺点主要有两个:

  • 需要在每个Shader中手动添加代码,不方便修改
  • 这种雾效的实现是固定的,无法定制效果,比如无法实现基于高度的雾效。

2.2 使用深度纹理实现高度雾

下面的例子我们通过深度纹理实现一种基于高度的全局无效,通过深度纹理重建每个像素的世界坐标,本次重建世界坐标使用的方法为射线插值,原理在【Unity Shader入门精要 第13章】使用深度和法线纹理(一)中也有介绍,然后根据高度控制雾的浓度,将原始颜色与雾的颜色进行混合。

测试脚本:

using UnityEngine;

public class PostEffect_HeightFog : PostEffectBase
{
    public Shader HeightFogShader;
    public Material HeightFogMat;

    public float FogBottom;
    public float FogTop;
    public float FogDensity;
    public Color FogColor;

    private Matrix4x4 mFrustumCornerRays;

    /// <summary>
    /// 计算纹理四个顶点的射线
    /// </summary>
    private void FillFrustumCornerRays()
    {
        //HalfHeight = | ToTop | = Near * Tangent(Fov / 2)
        //ToTop = Camera.Up * HalfHeight 
        //ToRight = Camera.Right * HalfHeight  * aspect
        float _halfHeight = Camera.nearClipPlane * Mathf.Tan(Camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
        Vector3 _toTop = Camera.transform.up * _halfHeight;
        Vector3 _toRight = Camera.transform.right * _halfHeight * Camera.aspect;

        //Scale = 1 / Near
        //Scaled_O_LD = ( Camera.Forward * Near - ToRight - ToTop ) * Scale
        //Scaled_O_RD = ( Camera.Forward * Near + ToRight - ToTop ) * Scale
        //Scaled_O_RU = ( Camera.Forward * Near + ToRight + ToTop ) * Scale
        //Scaled_O_LU = ( Camera.Forward * Near - ToRight + ToTop ) * Scale
        Vector3 _forwardVec = Camera.transform.forward * Camera.nearClipPlane;
        float _scale = 1 / Camera.nearClipPlane;
        mFrustumCornerRays.SetRow(0, (_forwardVec - _toRight - _toTop) * _scale); //左下
        mFrustumCornerRays.SetRow(1, (_forwardVec + _toRight - _toTop) * _scale); //右下
        mFrustumCornerRays.SetRow(2, (_forwardVec + _toRight + _toTop) * _scale); //右上
        mFrustumCornerRays.SetRow(3, (_forwardVec - _toRight + _toTop) * _scale); //左上
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Material _mat = CheckShaderAndMaterial(HeightFogShader, HeightFogMat);
        if (null == _mat) Graphics.Blit(src, dest);
        else
        {
            _mat.SetFloat("_FogBottom", FogBottom);
            _mat.SetFloat("_FogTop", FogTop);
            _mat.SetFloat("_FogDensity", FogDensity);
            _mat.SetColor("_FogColor", FogColor);
            FillFrustumCornerRays();
            _mat.SetMatrix("_FrustumCornersRay", mFrustumCornerRays);
            
            Graphics.Blit(src, dest, _mat);
        }
    }
}

测试Shader:

Shader "MyShader/Chapter_13/Chapter_13_HeightFog_Shader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        ZTest Always ZWrite Off Cull Off
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
                float3 scaledRay : TEXCOORD1;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            sampler2D _CameraDepthTexture;
            float4x4 _FrustumCornersRay;
            float _FogBottom;
            float _FogTop;
            float _FogDensity;
            fixed4 _FogColor;
            
            v2f vert(appdata_img v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv.xy = v.texcoord;
                o.uv.zw = v.texcoord;
                #if UNITY_UV_STARTS_AT_TOP 
                    if (_MainTex_TexelSize.y < 0)
                        o.uv.w = 1 - o.uv.w;
                #endif
                
                //判断当前处理的是四个顶点中的哪一个
                //然后选择对应的射线放入插值寄存器
                int _index;
                if(o.uv.x < 0.5 && o.uv.y < 0.5)
                    _index = 0; //左下
                else if(o.uv.x > 0.5 && o.uv.y < 0.5)
                    _index = 1; //右下
                else if(o.uv.x > 0.5 && o.uv.y > 0.5)
                    _index = 2; //右上
                else
                    _index = 3; //左上
                
                #if UNITY_UV_STARTS_AT_TOP
                    if (_MainTex_TexelSize.y < 0)
                        _index = 3 - _index;
                #endif
                o.scaledRay = _FrustumCornersRay[_index];
                    
                return o;
            }
            
            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 _samplerColor = tex2D(_MainTex, i.uv.xy);
                
                fixed _d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.zw);
                float _linearEyeDepth = LinearEyeDepth(_d);
                float3 _worldPos = _WorldSpaceCameraPos.xyz + i.scaledRay.xyz * _linearEyeDepth;
                
                float _ratio = (_FogTop - _worldPos.y) / (_FogTop - _FogBottom);
                float _fogDensity = saturate(_ratio * _FogDensity);
                
                fixed4 _finalColor = lerp(_samplerColor, _FogColor, _fogDensity);
                return fixed4(_finalColor.rgb, 1);
            }
        
            ENDCG
        }
        
    }
}

测试效果:
在这里插入图片描述

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

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

相关文章

LangChain开发【NL2SQL】应用

前言 关于LangGraph的简单介绍&#xff0c;请参考这篇博客&#xff1a; LangGraph开发Agent智能体应用【基础聊天机器人】-CSDN博客 对比LangChain实现NL2SQL 关于用LangChain开发NL2SQL的Agent应用&#xff0c;在这篇博客提供了完整的代码实现&#xff1a; LangChain开发…

数据结构笔记 线性表的查找 顺序,折半,分块查找

顺序查找&#xff1a;从头找到尾&#xff0c;或者从尾找到头 顺序查找的性能&#xff1a; 其中&#xff0c;辅助空间的O&#xff08;1&#xff09;用于存放哨兵的 折半查找&#xff1a;向下取整&#xff1a;指当计算的结果不为整数时取小于计算结果的整数。 折半查找的性能&am…

未来几年,同样的性能,推理功耗降低为现在的几万分之一,有可能吗

未来几年,同样的性能,推理功耗降低为现在的几万分之一,有可能吗 一.数据二.抓取LLM排行榜,相同的MMLU精度,模型参数量缩减倍数三.其它 有人说未来几年,推理功耗能降低为现在的几万分之一,好奇怎么能做到呢 一.数据 二.抓取LLM排行榜,相同的MMLU精度,模型参数量缩减倍数 import…

【LeetCode算法】第112题:路径总和

目录 一、题目描述 二、初次解答 三、官方解法 四、总结 一、题目描述 二、初次解答 1. 思路&#xff1a;二叉树先序遍历。首先访问根节点&#xff0c;若根节点是叶子节点并且值等于目标值&#xff0c;则返回true&#xff0c;否则递归访问左子树和右子树&#xff0c;只要左…

跨境电商|Facebook Marketplace怎么做?

2016 年&#xff0c;Facebook打造了同名平台 Facebook Marketplace。通过利用 Facebook 现有的庞大客户群&#xff0c;该平台取得了立竿见影的成功&#xff0c;每月访问量将超过 10 亿。对于个人卖家和小企业来说&#xff0c;Facebook Marketplace是一个不错的销货渠道&#xf…

激活乡村振兴新动能:推动农村产业融合发展,打造具有地方特色的美丽乡村,实现乡村全面振兴

目录 一、推动农村产业融合发展 1、农业产业链条的延伸 2、农业与旅游业的结合 二、挖掘地方特色&#xff0c;打造美丽乡村 1、保护和传承乡村文化 2、发展特色农业 三、加强基础设施建设&#xff0c;提升乡村品质 1、改善农村交通条件 2、提升农村水利设施 四、促进…

PHP“well”运动健身APP-计算机毕业设计源码87702

【摘要】 随着互联网的趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己的信息推广出去&#xff0c;最好方式就是建立自己的平台信息&#xff0c;并对其进行管理&#xff0c;随着现在智能手机的普及&#xff0c;人们对于智能手机里面的应用“well”运动健身app也在不断…

树莓派Pico开发板与Gravity语音识别模块接口及其语音控制MicroPython编程

**摘要:**介绍Gravity语音识别模块的主要功能及其特性,讲述树莓派Pico与Gravity语音识别模块接口连接的基本方法,介绍使用Gravity语音识别模块学习语音唤醒词/命令词并给出I2C通信接口语音识别MicroPython库,以及基于树莓派Pico开发板和Gravity语音识别模块的语音控制Micro…

通过在idea上搭建虚拟hadoop环境使用MapReduce做词频去重

idea上的MapReduce ​ 一般在开发中&#xff0c;若是等到环境搭配好了再进行测试或者统计数据&#xff0c;数据处理等操作&#xff0c;那会很耽误时间&#xff0c;所以一般都是2头跑&#xff0c;1波人去在客户机上搭建环境&#xff0c;1波人通过在idea上搭建虚拟hadoop环境&am…

祝大家端午节安康

五月到端午&#xff0c;愿你端来快乐&#xff0c;无烦无恼&#xff1b;端来好运&#xff0c;无时无刻&#xff1b;端来健康&#xff0c;无忧无虑&#xff1b;端来财富&#xff0c;五谷丰登&#xff1b;端来祝福&#xff0c;五彩缤纷。端午节安康&#xff01;

onesixtyone一键扫描SNMP服务(KALI工具系列二十)

目录 1、KALI LINUX 简介 2、onesixtyone工具简介 3、在KALI中使用onesixtyone 3.1 目标主机IP&#xff08;win&#xff09; 3.2 KALI的IP 4、操作示例 4.1 扫描目标主机 4.2 加上团队名称 4.3 输出详细结果 4.4 扫描整个网段 5、总结 1、KALI LINUX 简介 Kali Lin…

淘宝扭蛋机小程序,扭蛋市场创新模式

扭蛋机作为潮玩市场的娱乐消费方式&#xff0c;成为了当下消费者的新宠。扭蛋机凭借自身性价比高、商品多样、惊喜性等特点&#xff0c;吸引了各个年龄层的消费者&#xff0c;不仅年轻人喜欢&#xff0c;不少小学生和老年人也非常喜欢&#xff0c;扭蛋机市场迎来了快速发展期。…

简单介绍一下vim

简单介绍一下vim 一、vim是什么&#xff1f;二、vim的优点三、vi/vim的使用命令模式输入模式底线命令模式 四、vi/vim 按键说明&#xff08;一&#xff09;命令模式可用的光标移动、复制粘贴、搜索替换等移动光标的方法:搜索替换的方法删除、复制与贴上的方法 &#xff08;二&a…

Vue15-watch对比计算属性

一、姓名案例 1-1、watch实现 1-2、计算属性 对比发现&#xff1a; 计算属性比watch属性更简略一些。 1-3、计算属性 VS 侦听属性 1-4、需求变更 计算属性中不能开启异步任务&#xff01;&#xff01;&#xff01;因为计算属性靠return返回值。但是watch靠亲自写代码去改。 1-…

msvcp140_CODECVT_IDS.dll的解决方法是什么?有多少种解决方法

msvcp140_CODECVT_IDS.dll 是一个动态链接库&#xff08;DLL&#xff09;文件&#xff0c;属于微软Visual C 2015运行时库的一部分。这个文件主要负责字符编码转换&#xff0c;支持Unicode与其他字符集之间的转换&#xff0c;如UTF-8与UTF-16。它对于运行时库的多语言支持至关重…

【Python】在【数据挖掘】与【机器学习】中的应用:从基础到【AI大模型】

目录 &#x1f497;一、Python在数据挖掘中的应用&#x1f495; &#x1f496;1.1 数据预处理&#x1f49e; &#x1f496;1.2 特征工程&#x1f495; &#x1f497;二、Python在机器学习中的应用&#x1f495; &#x1f496;2.1 监督学习&#x1f49e; &#x1f496;2.2…

cs与msf权限传递

cs传递到msf 1&#xff0c;先启动cs ┌──(root㉿ring04h)-[~/cobalt_strike_4.7] └─# ./teamserver 192.168.196.144 123456 ​ ┌──(root㉿ring04h)-[~/cobalt_strike_4.7] └─# ./start.sh ​ 2&#xff0c;上传木马&#xff0c;上线主机 3&#xff0c;msf配置一个…

Springboot健身房管理系统-计算机毕业设计源码44394

摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在健身房管理的要求下&#xff0c;开发一款整体式结构的健身房管理系统…

Unity HoloLens2 MRTK 空间锚点 基础教程

Unity HoloLens2 MRTK 空间锚点 基础教程 Unity HoloLens2 空间锚点MRTK 空间锚点 准备Unity 工程创建设置切换 UWP 平台UWP 平台设置 下载并安装混合现实功能工具导入混合现实工具包和 OpenXR 包 Unity 编辑器 UWP 设置Unity 2019.4.40 设置Unity 2022.3.0 设置Unity 2022.3.0…

【数据结构(邓俊辉)学习笔记】图04——双连通域分解

文章目录 0. 概述1 关节点与双连通域2 蛮力算法3 可行算法4 实现5 示例6 复杂度 0. 概述 学习下双连通域分解&#xff0c;这里略微有一点点难&#xff0c;这个算是DFS算法的非常非常经典的应用&#xff0c;解决的问题也非常非常有用。 1 关节点与双连通域 连通性很好理解&am…