【Unity Shader入门精要 第12章】屏幕后处理效果(三)

1. Bloom效果

Bloom描述的是图像中较亮的部分向周围一定范围内发生扩散,造成一种朦胧的效果,常用于表现游戏中的灯光或隧道出口之类的效果。

下面的例子将实现一个简单的Bloom效果,其原理是:

  • 将原始图像中较亮(灰度值超过阈值)的部分提取出来,存储到一张临时纹理上
  • 对提取出来的亮部做高斯模糊,使图像发散
  • 将高斯模糊过的亮部与原始图像进行叠加

这里我们第一次涉及到图像翻转的问题:由于OpenGL平台和DX平台对屏幕空间的Y轴方向定义是相反的,而Unity的渲染底层是基于OpenGL标准,因此我们在Unity中抓取的屏幕图像,在DX平台上直接使用就存在上下颠倒的问题。为了避免这种情况,在大部分情况下Unity会在Graphics.Blit时自动进行翻转,但像本次Bloom效果这种,在Blit中需要对多张纹理进行采样时,就需要我们手动进行翻转

#if UNITY_UV_STARTS_AT_TOP
	if(_MainTex_TexelSize.y < 0)
		o.uv.w = 1 - o.uv.w;
#endif

测试脚本

using UnityEngine;

public class PostEffect_Bloom : PostEffectBase
{
    public Shader BloomShader;
    public Material BlooMaterial;

    [Range(0.0f, 1.0f)]
    public float BrightThreshold = 0.5f;
    [Range(1, 8)]
    public int DowmSampler = 4;
    [Range(0.1f, 3.0f)]
    public float SmaplerStep = 1;
    [Range(1, 8)]
    public int BlurRound = 1;

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Material _mat = CheckShaderAndMaterial(BloomShader, BlooMaterial);
        if (null == _mat) Graphics.Blit(src, dest);
        else
        {
            _mat.SetFloat("_BrightThreshold", BrightThreshold);

            int _width = src.width / DowmSampler;
            int _height = src.height / DowmSampler;
            RenderTexture _buffer_0 = RenderTexture.GetTemporary(_width, _height, 0);
            _buffer_0.filterMode = FilterMode.Bilinear;
            
            //用第一个Pass将亮部提取出来
            Graphics.Blit(src, _buffer_0, _mat, 0);
            for (int i = 0; i < BlurRound; i++)
            {
                _mat.SetFloat("_SmaplerStep", i * SmaplerStep);
                
                //使用第二个Pass横向滤波
                RenderTexture _buffer_1 = RenderTexture.GetTemporary(_width, _height, 0);
                Graphics.Blit(_buffer_0, _buffer_1, _mat, 1);
                RenderTexture.ReleaseTemporary(_buffer_0);
                _buffer_0 = _buffer_1;
                
                //使用第三个Pass纵向滤波
                _buffer_1 = RenderTexture.GetTemporary(_width, _height, 0);
                Graphics.Blit(_buffer_0, _buffer_1, _mat, 2);
                RenderTexture.ReleaseTemporary(_buffer_0);
                _buffer_0 = _buffer_1;
            }
            
            //使用第四个Pass将处理后的亮部与原始图像混合
            _mat.SetTexture("_BloomTex", _buffer_0);
            Graphics.Blit(src, dest, _mat, 3);
            RenderTexture.ReleaseTemporary(_buffer_0);
            
            Graphics.Blit(src, dest, _mat);
        }
    }
}

测试Shader

Shader "MyShader/Chapter_12/Chapter_12_Bloom_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
    }
    SubShader
    {
        ZTest Always ZWrite Off Cull Off
        
        CGINCLUDE
            #include "UnityCG.cginc"
            
            sampler2D _MainTex;
            float4 _MainTex_TexelSize;
            fixed _BrightThreshold;
            sampler2D _BloomTex;
            half _SmaplerStep;
            
            struct v2f_ExtractBright
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
            
            v2f_ExtractBright vert_ExtractBright(appdata_img v)
            {
                v2f_ExtractBright o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.texcoord;
                return o;
            }
            
            fixed Luminance(fixed4 _color)
            {
                return 0.2125 * _color.r + 0.7154 * _color.g + 0.0721 * _color.b;
            }
            
            fixed4 frag_ExtractBright(v2f_ExtractBright i) : SV_Target
            {
                fixed4 _samplerColor = tex2D(_MainTex, i.uv);
                fixed _fixValue = clamp(Luminance(_samplerColor) - _BrightThreshold, 0, 1);
                return _samplerColor * _fixValue;
            }
            
            struct v2f_Mix
            {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };
            
            v2f_Mix vert_Mix(appdata_img v)
            {
                v2f_Mix 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_Mix(v2f_Mix i) : SV_Target
            {
                return tex2D(_MainTex, i.uv.xy) + tex2D(_BloomTex, i.uv.zw);
            }
        
        ENDCG
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert_ExtractBright
            #pragma fragment frag_ExtractBright   
            ENDCG
        }
        
        //调用已有的高斯模糊PASS
        UsePass "MyShader/Chapter_12/Chapter_12_GaussianBlur_Shader/GAUSSIAN_BLUR_HORIZENTAL"
        UsePass "MyShader/Chapter_12/Chapter_12_GaussianBlur_Shader/GAUSSIAN_BLUR_VERTICAL"
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert_Mix
            #pragma fragment frag_Mix   
            ENDCG
        }
    }
}

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

2. 运动模糊

运动模糊的原理是,当物体发生位置变化时,前后帧的影像发生叠加,造成模糊的效果。通过运动模糊可以更好的表现速度感。

下面的例子将通过累积缓存的方式实现一个运动模糊的效果:

  • 创建一张临时纹理,用于记录已经渲染过的图像,即过去帧的影像
  • 设置过去帧的混合系数(MixAmount),用于控制过去帧的模糊影像的清晰程度
  • 在第一个 Pass 中将当前帧的影像与保存的过去帧图像进行混合,混合系数为(1 - 过去帧的 MixAmount),这一步的目的只是为了在同一张图片上能够同时表现出物体在不同时刻的位置,因此我们只需要表现物体颜色的 RGB 部分,而并不希望混合时也改变 Alpha 值,这里可以通过 ColorMask RGB 实现只写入 RGB 通道的值
  • 最终渲染图像的 Alpha 值应以当前帧的实际 Alpha 值为准,因此在第二个 Pass 中再次对当前帧原始图像进行采样,通过 Color Mask A 只将Alpha值写入

测试脚本

using UnityEngine;

public class PostEffect_MotionBlur : PostEffectBase
{
    public Shader MotionBlurShader;
    public Material MotionBlurMat;
    
    /// <summary>
    /// 已渲染图像在混合中的权重
    /// </summary>
    [Range(0, 1)]
    public float PreviousAmount = 0.5f;

    //用于记录已经渲染的图像
    private RenderTexture mAccRT;

    private void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Material _mat = CheckShaderAndMaterial(MotionBlurShader, MotionBlurMat);
        if(null == _mat) Graphics.Blit(src, dest);
        else
        {
            CheckAccRT(src.width, src.height);
            _mat.SetFloat("_MixAmount", 1 - PreviousAmount);
            
            //调用 Shader 进行混合
            //先将结果混合到 AccRT 中,用于保存结果给下一帧使用
            Graphics.Blit(src, mAccRT, _mat);
            
            //再将混合结果输出
            Graphics.Blit(mAccRT, dest);
        }
    }

    /// <summary>
    /// 检测当前 AccRT 是否已创建以及是否与本帧尺寸一致
    /// </summary>
    /// <param name="_width"></param>
    /// <param name="_height"></param>
    private void CheckAccRT(int _width, int _height)
    {
        if (null == mAccRT || _width != mAccRT.width || _height != mAccRT.height)
        {
            DestroyImmediate(mAccRT);
            mAccRT = new RenderTexture(_width, _height, 0);
            mAccRT.hideFlags = HideFlags.HideAndDontSave;
        }
    }

    private void OnDestroy()
    {
        DestroyImmediate(mAccRT);
    }
}

测试Shader

Shader "MyShader/Chapter_12/Chapter_12_MotionBlur_Shader"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white"{}
    }
    SubShader
    {
        ZTest Always ZWrite Off Cull Off
        
        CGINCLUDE
        #include "UnityCG.cginc"
        
        sampler2D _MainTex;
        fixed _MixAmount;
        
        struct v2f
        {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };
        
        v2f vert(appdata_img v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.texcoord;
            return o;
        }
        
        fixed4 frag_RGB(v2f i) : SV_Target
        {
            return fixed4(tex2D(_MainTex, i.uv).rgb, _MixAmount);
        }
        
        fixed4 frag_A(v2f i) : SV_Target
        {
            return tex2D(_MainTex, i.uv);
        }
        
        ENDCG
        
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask RGB
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag_RGB
            ENDCG
        }
        
        Pass
        {
            Blend One Zero
            ColorMask A
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag_A
            ENDCG
        }
    }
}

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

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

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

相关文章

Word2021中的The Mathtype DLL cannot be found问题解决(office 16+mathtype7+非初次安装)

问题描述&#xff0c;我的问题发生在word中无法使用自定义功能区中的mathtype 我的环境是&#xff1a;W11Word2021mathtype7 因为我是第二次安装mathtype7&#xff0c;所以我怀疑是因为没有卸载干净&#xff0c;于是我参考了下面这篇文章的做法 参考文章 1.首先重新卸载当前的…

IO流---字节流.Java

一&#xff0c;概述 IO流是存储和读取数据的解决方案。 I&#xff1a;input O:output流&#xff1a;像水流一样传输数据 因为IO流与File是息息相关的&#xff0c;所以在学习IO流之前&#xff0c;简单回顾一下File&#xff1a;&#x1f604;&#x1f60a;&#…

数据结构--数组(详细分析)

目录 &#x1f349;引言 &#x1f349;数组 &#x1f348;数组的特性 &#x1f348;数组的优缺点 &#x1f34d;优点&#xff1a; &#x1f34d;缺点&#xff1a; &#x1f348;数组的声明与初始化 &#x1f348;数组的常见操作 &#x1f34d; 插入操作 &#x1f34d;…

QTP——功能测试

一、前言&#xff08;课设目的及内容&#xff09; QTP是quicktest Professional的简称&#xff0c;是一种自动测试工具。使用QTP的目的是想用它来执行重复的手动测试&#xff0c;主要是用于回归测试和测试同一软件的新版本。因此你在测试前要考虑好如何对应用程序进行测试&…

46、Flink 的 异步 I/O 算子详解

异步 I/O 1.需求 在与外部系统交互&#xff08;用数据库中的数据扩充流数据&#xff09;时&#xff0c;需要考虑与外部系统的通信延迟对整个流处理应用的影响。 同步交互&#xff1a;使用 MapFunction访问外部数据库的数据&#xff0c; MapFunction 向数据库发送一个请求然后…

企业软件产品和服务 之 设计保证安全 七项承诺

1. 引言 公司如何保护自己免受数据泄露的影响&#xff1f;标准答案就是&#xff1a; “启用多因素身份验证”——MTA&#xff08;Enable multifactor authentication&#xff09;。 但是&#xff0c;目前很多公司仍然盲目地只使用密码作为唯一的身份来源。 网络安全的核心是…

IPD推行成功的核心要素(九)需求管理助力产品从一次成功走向一直成功

在当今竞争激烈的商业环境中&#xff0c;项目的成功与否往往取决于其能否满足用户和利益相关者的需求。然而&#xff0c;理解、捕捉和有效管理这些需求并非易事。因此&#xff0c;需求管理在项目管理中扮演着至关重要的角色。需求管理是一个系统性的过程&#xff0c;旨在确保项…

直播分享|深入解析ts-morph:通过注释生成类型文档

♪ ♫ 你看小狗在叫 树叶会笑 风声在呢喃♫ ♪ 乘风追梦&#xff0c;童心未泯 OpenTiny 预祝所有大朋友、小朋友儿童节快乐~ 与此同时&#xff0c;OpenTiny 贡献者直播也即将开启啦~ 直播主题&#xff1a;【深入解析ts-morph&#xff1a;通过注释生成类型文档】 6月1日&am…

前驱图,程序执行和进程状态

目录 前驱图 程序的执行 顺序执行 并发执行 进程的定义 进程的状态 总结 前驱图 现在有两个任务分别为p1,p2; 只有执行了p1,才可以执行p2&#xff0c;此时可以称p1为p2的前驱。通过符号语言表示如下&#xff1a; p1->p2 程序的执行 下面引进一段代码来理解进程的概念…

IDEA 学习之 疑难杂症系列

IDEA 学习之 疑难杂症系列 1. Mapstruct 编译空指针问题 1.1. 现象 NullPointerException at org.mapstruct.ap.internal.processor.DefaultVersionInformation.createManifest1.2. 原因 MapStruct 在 IDEA 2020.3 版本编译 NPE 问题 1.3. 解决办法 2. IDEA 学习之 编译内…

什么牌子的开放式耳机质量好?2024超强实力派品牌推荐!

耳机对于一个音乐人有重要这个不必多说&#xff0c;我朋友是个音乐编辑&#xff0c;他经常需要长时间佩戴耳机进行音频编辑和混音工作。在尝试过多款开放式耳机后&#xff0c;都没找到合适的。今天&#xff0c;我将从专业角度为大家带来几款热门开放式耳机的测评报告&#xff0…

Python 高级数据类型

列表List 定义列表 可以将不同的基本数据类型或者列表装到一个列表里 my_list [1,2,3,4,5] print(my_list) # [1, 2, 3, 4, 5] 直接打印出列表的内容 print(type(my_list)) # <class list>my_list ["1","2","3","4","…

MYSQL之安装

一&#xff0c;下载仓库包 wget -i -c https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm二&#xff0c;安装仓库 yum -y install mysql80-community-release-el7-3.noarch.rpmsed -i s/gpgcheck1/gpgcheck0/g mysql-community.repo三&#xff0c;安装MY…

免费SSL证书的安全性与获取指南

SSL证书是一种数字凭证&#xff0c;用于加密用户与网站之间的信息交换&#xff0c;以确保传输的数据不被第三方窃取。它像是一个数字版的密封印章&#xff0c;为数据的传输过程提供了一层保护膜。 免费的SSL证书通常由CA机构提供&#xff0c;它们同样可以提供基础数据的加密服…

瑞吉外卖项目整体介绍

黑马程序员瑞吉外卖 文章目录 一、项目介绍二、产品原型展示三、技术选型四、功能架构五、角色 一、项目介绍 二、产品原型展示 产品原型&#xff0c;就是一款韩品成型之前的一个简单的框架&#xff0c;就是将页面的排版布局展现出来&#xff0c;使产品的初步构思有一个可视化…

跟着大佬学RE(一)

学了一个 map&#xff08;&#xff09;函数的使用 import base64rawData "e3nifIH9b_CndH" target list(map(ord, rawData)) # map 函数将 rawData 中的每个字符传递给 ord 函数。ord 函数返回给定字符的 Unicode 码点 print(target) # 打印 map 对象的内存地址&…

Prism 入门02,区域介绍

一.区域概念和使用方式 什么是区域(Region)?区域,在Prism 框架中,区域是模块化的核心功能之一,其主要作用是降低应用程序和模块之间的耦合度 。使用方式:在应用程序的界面中,划分出某块区域,并为这个区域定义一个唯一的区域名称。那么通过这个区域名称,应用程序就可以…

Android Display Graphics #1 整体框架介绍一

软件基础 Android的framework层提供了一系列的图像渲染API&#xff0c;可绘制2D和3D。简单理解就是上层开发APP的小伙伴提供了接口&#xff0c;开发者可以直接显示对应的自己内容。但如果掌握了Display底层逻辑再写上层app&#xff0c;会有掌控力&#xff0c;出问题可以根据lo…

【Mybatis】源码分析-自定义框架

1、自定义持久层框架 1.1、分析JDBC操作问题 package blnp.net.cn.jvm.demos;import java.sql.*;/*** <p></p>** author lyb 2045165565qq.com* createDate 2024/5/24 14:24*/ public class JdbcTest {public static void main(String[] args) {Connection conne…

大模型+RAG,全面介绍!

1 介绍 大型语言模型&#xff08;LLMs&#xff09;在处理特定领域或高度专业化的查询时存在局限性**&#xff0c;如生成不正确信息或“幻觉”。**缓解这些限制的一种有前途的方法是检索增强生成&#xff08;RAG&#xff09;&#xff0c;RAG就像是一个外挂&#xff0c;将外部数…