Unity自定义后处理——Bloom效果

news2024/10/7 14:27:57

大家好,我是阿赵。
  继续介绍屏幕后处理效果,这一期讲一下Bloom效果。

一、Bloom效果介绍

在这里插入图片描述

还是拿这个模型作为背景。
在这里插入图片描述

Bloom效果,就是一种全屏泛光的效果,让模型和特效有一种真的在发光的感觉。

在这里插入图片描述
在这里插入图片描述

根据参数不一样,可以做出不同的发光效果。

二、原理介绍

  之前在介绍模糊效果的时候说过,Bloom效果也是基于模糊效果制作的。
  Bloom的原理很简单,先用模糊处理,算出一张模糊后的图片,然后把这张图片的RGB和原始屏幕图片的RGB相加就可以了。由于是RGB颜色叠加,所以本身图片颜色越接近白色的地方,越容易爆掉,所以越接近白色的地方发光的感觉就越明显。如果想Bloom的效果更爆一点,就自己对模糊后的图片进一步做处理吧,最常见的手段就是先乘后加,或者Power后再相加也可以
在这里插入图片描述

实现很简单,在原来的模糊效果代码上面,我们应该再加多一个叠加的Shader就可以了。

half4 frag (v2f_img i) : SV_Target
{
    half4 col = tex2D(_MainTex, i.uv);
	half4 brightCol = tex2D(_brightTex, i.uv);
	col.rgb += brightCol.rgb;
    return col;
}

  我这里在处理模糊之前,先加多了一个BrightRange的计算。这是为了控制一下发光的范围。BrightRange的算法很简单,先取原始图片的rgb三个通道颜色值最大的通道的值,然后用这个最大值减去一个我们指定的值。最后把原图的rgb颜色乘以这个值就可以了。

fixed4 frag (v2f_img i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);
	float br = max(max(col.r, col.g), col.b);
	br = max(0, (br - _BrightCut)) / max(br, 0.00001);
	col.rgb *= br;
    return col;
}

通过这个值,我们就可以更好的控制发光的范围
三、源码
1、C#部分

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BloomCtrl : MonoBehaviour
{
    private Material blurMat;
    private Material brightMat;
    private Material bloomMat;
    public bool isBlur = false;
    [Range(0, 4)]
    public float blurSize = 0;
    [Range(-3, 3)]
    public float blurOffset = 1;
    [Range(1, 3)]
    public int blurType = 3;
    [Range(0, 1)]
    public float brightCut = 0.5f;
    void Start()
    {

    }


    void Update()
    {

    }

    private Material GetBlurMat(int bType)
    {
        if (bType == 1)
        {
            return new Material(Shader.Find("Hidden/AzhaoBoxBlur"));
        }
        else if (bType == 2)
        {
            return new Material(Shader.Find("Hidden/AzhaoGaussianBlur"));
        }
        else if (bType == 3)
        {
            return new Material(Shader.Find("Hidden/AzhaoKawaseBlur"));
        }
        else
        {
            return null;
        }
    }

    private void ReleaseRT(RenderTexture rt)
    {
        if (rt != null)
        {
            RenderTexture.ReleaseTemporary(rt);
        }
    }

    private bool CheckNeedCreateBlurMat(Material mat, int bType)
    {
        if (mat == null)
        {
            return true;
        }
        if (mat.shader == null)
        {
            return true;
        }
        if (bType == 1)
        {
            if (mat.shader.name != "Hidden/AzhaoBoxBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (bType == 2)
        {
            if (mat.shader.name != "Hidden/AzhaoGaussianBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else if (bType == 3)
        {
            if (mat.shader.name != "Hidden/AzhaoKawaseBlur")
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }
    private void BlurFun(RenderTexture source, RenderTexture destination, float blurTime, int bType, float offset)
    {
        if (CheckNeedCreateBlurMat(blurMat, bType) == true)
        {
            blurMat = GetBlurMat(bType);
        }
        if (blurMat == null || blurMat.shader == null || blurMat.shader.isSupported == false)
        {
            return;
        }
        blurMat.SetFloat("_BlurOffset", offset);
        float width = source.width;
        float height = source.height;
        int w = Mathf.FloorToInt(width);
        int h = Mathf.FloorToInt(height);
        RenderTexture rt1 = RenderTexture.GetTemporary(w, h);
        RenderTexture rt2 = RenderTexture.GetTemporary(w, h);
        Graphics.Blit(source, rt1);
        for (int i = 0; i < blurTime; i++)
        {
            ReleaseRT(rt2);
            width = width / 2;
            height = height / 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            rt2 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt1, rt2, blurMat, 0);
            width = width / 2;
            height = height / 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            ReleaseRT(rt1);
            rt1 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt2, rt1, blurMat, 1);
        }
        for (int i = 0; i < blurTime; i++)
        {
            ReleaseRT(rt2);
            width = width * 2;
            height = height * 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            rt2 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt1, rt2, blurMat, 0);
            width = width * 2;
            height = height * 2;
            w = Mathf.FloorToInt(width);
            h = Mathf.FloorToInt(height);
            ReleaseRT(rt1);
            rt1 = RenderTexture.GetTemporary(w, h);
            Graphics.Blit(rt2, rt1, blurMat, 1);
        }
        Graphics.Blit(rt1, destination);
        ReleaseRT(rt1);
        rt1 = null;
        ReleaseRT(rt2);
        rt2 = null;
        return;
    }
    private bool BrightRangeFun(RenderTexture source, RenderTexture destination)
    {
        if (brightMat == null)
        {
            brightMat = new Material(Shader.Find("Hidden/BrightRange"));
        }
        if (brightMat == null || brightMat.shader == null || brightMat.shader.isSupported == false)
        {
            return false;
        }
        brightMat.SetFloat("_BrightCut", brightCut);
        Graphics.Blit(source, destination, brightMat);
        return true;

    }

    private bool BloomAddFun(RenderTexture source, RenderTexture destination, RenderTexture brightTex)
    {
        if (bloomMat == null)
        {
            bloomMat = new Material(Shader.Find("Hidden/AzhaoBloom"));
        }
        if (bloomMat == null || bloomMat.shader == null || bloomMat.shader.isSupported == false)
        {
            return false;
        }
        bloomMat.SetTexture("_brightTex", brightTex);
        Graphics.Blit(source, destination, bloomMat);
        return true;
    }
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (isBlur == true)
        {
            RenderTexture finalRt = source;
            RenderTexture rt2 = RenderTexture.GetTemporary(source.width, source.height);
            RenderTexture rt3 = RenderTexture.GetTemporary(source.width, source.height);
            BrightRangeFun(source, rt2);
            if (blurSize > 0)
            {
                BlurFun(rt2, rt3, blurSize, blurType, blurOffset);
                BloomAddFun(source, finalRt, rt3);
            }
            Graphics.Blit(finalRt, destination);
            ReleaseRT(finalRt);
            ReleaseRT(rt2);
            ReleaseRT(rt3);

        }
        else
        {
            Graphics.Blit(source, destination);
        }
    }
}

2、Shader
  模糊shader就不重复了,参考之前的文章
这里提供一下Bloom和BrightRange的shader
1.Bloom

Shader "Hidden/AzhaoBloom"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_brightTex("BrightTex",2D) = "black"
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag

            #include "UnityCG.cginc"



            sampler2D _MainTex;
		sampler2D _brightTex;

			half4 frag (v2f_img i) : SV_Target
            {
                half4 col = tex2D(_MainTex, i.uv);
				half4 brightCol = tex2D(_brightTex, i.uv);
				col.rgb += brightCol.rgb;
                return col;
            }
            ENDCG
        }
    }
}

2.BrightRange

Shader "Hidden/BrightRange"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_BrightCut("LightVal",Range(0,1)) = 0.5
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag

            #include "UnityCG.cginc"



            sampler2D _MainTex;
			float _BrightCut;

            fixed4 frag (v2f_img i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
				float br = max(max(col.r, col.g), col.b);
				br = max(0, (br - _BrightCut)) / max(br, 0.00001);
				col.rgb *= br;
                return col;
            }
            ENDCG
        }
    }
}

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

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

相关文章

21 对于对象中set方法的理解

对于一个Student对象&#xff0c;属性有name和age&#xff0c;而age一般是比0大的&#xff0c;赋值不可能是负数&#xff0c;可以通过set方法来控制&#xff01;Student对象如下&#xff1a; public class Student {// 成员变量private int age;public Student() {}public int…

嬴图 | K邻算法在风险传导场景中的实践意义

随着图思维方式与图数据框架在工业领域的有效开展和深入应用&#xff0c;可靠且高效的图算法也就成为了图数据探索、挖掘与应用的基石。该图算法系列&#xff08;具体见推荐阅读&#xff09;&#xff0c;集合了 Ultipa 嬴图团队在算法实践应用中的经验与思考&#xff0c;希望在…

海外NFT玩法入门科普 - Web3.0(二)

咪哥杂谈 本篇阅读时间约为 7 分钟。 1 前言 距离上一篇文章有些时日&#xff0c;把上一次介绍 web3 的海外 nft 的坑继续填完&#xff0c;今天这篇是下篇&#xff0c;其中详细的介绍了我从去年一年来&#xff0c;所见&#xff0c;所参与的玩法&#xff0c;赚钱方式&#xff0c…

VUE开发神器-NVM nodejs版本控制工具nvm

前言 在学习前端框架或者是nodejs时&#xff0c;有时候某些框架对nodejs的版本有要求。但此时你的电脑上已经安装了10.x版本的nodejs&#xff0c;你不想直接更新到12.x&#xff0c;想同时保存10.x和12.x版本&#xff0c;在必要的时候还能随时切换nodejs版本。那么nvm工具你值得…

[oeasy]python0073_进制转化_eval_evaluate_衡量_oct_octal_八进制

进制转化 回忆上次内容 上次了解的是 整型数字类变量integer前缀为i 整型变量 和 字符串变量 不同 整型变量 是 直接存储二进制形式的可以用 int()函数 将 2进制形式的 字符串 转化为 十进制整数 int()函数 接受两个变量 待转化的字符串字符串使用的进制 二进制 和 十进制…

No spring.config.import property has been defined

报错如下&#xff0c;异常信息里面已经提示了修改的方式&#xff1a; 19:16:46.221 [main] ERROR org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter - *************************** APPLICATION FAILED TO START ***************************Description…

数据库应用:MySQL高级语句(三)存储过程

目录 一、理论 1.存储过程 2.存储过程操作 3.存储过程的参数 4.存储过程的控制语句 二、实验 1.创建和调用存储过程 ​编辑 ​编辑 2.存储过程的参数 3.存储过程的控制语句 三、总结 一、理论 1.存储过程 &#xff08;1&#xff09;概念 存储过程是一组为了完成…

「旅游小攻略」广东河源

Hello 小伙伴们好呀&#xff0c;我是爱折腾的 jsliang~ 今天主要安利的&#xff0c;是「广东省/河源市/源城区」附近的逛吃逛吃。 特别适合 2 天 1 夜、3 天 2 夜的&#xff0c;自驾游或者随心走的小伙伴&#xff0c;随着本篇攻略嗨起来吧~ 更多了解欢迎加 WX&#xff1a;Liang…

DBSCAN 算法详解 + 代码实现 + 参数的选择

基于密度的噪声应用空间聚类&#xff08;DBSCAN&#xff09;是一种无监督聚类算法&#xff0c;它可以替代KMeans和层次聚类等流行的聚类算法。 KMeans 的缺点 容易受到异常值的影响&#xff0c;离群值对质心的移动方式有显著的影响。在集群大小和密度不同的情况下存在数据精确…

2023-07-23 LeetCode每日一题(接雨水)

2023-07-23每日一题 一、题目编号 42. 接雨水二、题目链接 点击跳转到题目位置 三、题目描述 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 提示&#xff1a; n height.length1 < n < 2 …

ChatGPT 4.0 —— Code Interpreter

&#x1f4ce;产品销售数据集.csv 选取以上的数据集作为输入&#xff0c;对Code Interpreter 进行测试 1.输入指定数据集&#xff0c;要求给出该数据集的概貌 2.请分析销售的总金额和其他变量的关系 Python Script: # Import required libraries import matplotlib.pyplot a…

UE5 用DLL文件制作第三方插件

本篇博文介绍了&#xff0c;如果在UE 中如何使用第三方库&#xff0c;及制作成插件的方法。 DLL 文件是上篇文章中创键的具体的方法见上篇文章。下面开始介绍方法 首先&#xff0c;创建一个空白的 UE5 C 项目&#xff0c;然后再创建一个空白内容的插件&#xff0c;如下图所示 …

转义字符\

转义字符就是反斜杠想要实现的转义功能首字母。 为什么需要转义字符&#xff1f; 当字符串中包含反斜杠、单引号和双引号等有特殊用途的字符时&#xff0c;必须使用反斜杠对这些字符进行转义&#xff08;转换一个含义&#xff09; 反斜杠&#xff1a;\ 单引号&#xff1a;’ 双…

HCIA动态路由基础实验(eNSP)

实验题目及要求&#xff1a; IP配置&#xff1a; R1: <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysname r1 [r1]int GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip add 192.168.1.1 30 Jul 22 2023 13:07:24-08:00 r1 %%01IFNET/4/…

构建Web3生态系统:区块链技术与数字管理的融合

Web3技术是一种基于区块链技术的下一代互联网技术&#xff0c;它致力于实现去中心化、安全和透明的互联网世界。在Web3生态系统中&#xff0c;区块链技术是基础设施&#xff0c;而浏览器和数字管理是主要的应用场景。 区块链技术是Web3的核心&#xff0c;它是一种去中心化的分…

Spring更简单读取和存储对象

目录 前言 注解 存储Bean 通过类注解 配置扫描路径 添加类注解存储Bean对象 Controller(控制器存储) Service(服务存储) Repository(仓库存储) Component(组件存储) Configuration(配置存储) 类注解之间的关系 Bean的命名规则 通过方法注解 重命名Bean 方式一 方式…

【【51单片机 --秒表--定时器扫描按键数码管】】

轻松做秒表&#xff0c;谁用谁知道 我们在Key 和Nixie 内部都写一个函数这个是main 中中断函数的调用 因为中断是有优先级的&#xff0c;假设有多个中断&#xff0c;那么总是优先级高的在进行&#xff0c;如果我们安排多个中断的话&#xff0c;整体设计就会变得很麻烦 我们放在…

K8s系列---【K8s如何配置优雅停机?】

K8s如何配置优雅停机&#xff1f; 应用部署在k8s中&#xff0c;需要设置pod的优雅停机时间(terminationGracePeriodSeconds)&#xff0c;一般大于应用程序中spring.lifecycle.timeout-per-shutdown-phase设置的超时时间&#xff1b;设置之后服务更新或者重启时k8s会捕获到1号进…

2 push方法的使用(相当于python的append方法)

push方法相当于python的append方法&#xff0c;用来添加数组元素。 另外&#xff0c;数组取元素也是使用data[i]的格式。 例子&#xff1a; <script>var dataList [[1,2,3,4,5,6],[7,8,9,1,2,3]];var x dataList[0];console.log(x);dataList.push([1,1,2,3,4,5]);cons…

leetcode 47. 全排列 II

2023.7.23 这道题是上一题全排列 的一个升级版。 唯一区别就是需要增加一个树层去重的操作&#xff0c;因为数组nums中允许有重复的元素了&#xff0c;而上一题没有重复元素。 下面看代码&#xff1a; class Solution { public:vector<vector<int>> ans;vector<…