【TA100】Bloom算法

Bloom算法是一种常见的图像处理技术,用于模拟摄像机的辉光效果,使明亮区域扩散到周围,增加视觉的朦胧感。它通过提取原图较亮部分,进行高斯模糊处理,然后与原图混合。高动态范围(HDR)和低动态范围(LDR)在Bloom效果中起到关键作用,而高斯模糊是实现图像模糊的关键步骤。文章还提供了Bloom算法在游戏和图形学中的应用实例和参考资料。
摘要由CSDN通过智能技术生成

一、什么是Bloom算法

1、首先看一下Bloom效果长什么样

在这里插入图片描述

2、什么是Bloom

● Bloom,也称辉光,是一种常见的屏幕效果
● 模拟摄像机的一种图像效果,让画面中较亮的区域“扩散”到周围的区域中,造成一种朦胧的效果
● 可以让物体具有真实的明亮效果
● 可以实现光晕效果
在这里插入图片描述

3、Bloom的实现原理

①Bloom实现原理
● 实现思路:
○ 1.提取原图较亮区域(利用阈值)
○ 2.模糊该图像
○ 3.与原图混合/叠加
在这里插入图片描述

● /在HDR和LDR的那节课中也提到过bloom,我直接把当时的作的一张流程图摘过来以供参考:
○
②前置知识1:HDR和LDR
● HDR和LDR分别是是高动态范围和低动态范围的缩写
● LDR
○ jpg、png格式图片
○ RGB在[0,1]

● HDR
○ HDR、EXR格式图片
○ 可以超过1
● 因为自然界中的亮度差异是很大的(比如蜡烛的光强度约为15,而太阳的强度约为10w),只用LDR的话很多效果完全表现不出来

③前置知识2:高斯模糊
● 实现图像模糊的一种方式
● 高斯模糊:
○ 利用高斯核进行卷积运算,得到模糊的图像
在这里插入图片描述
高斯核
○ 高斯核:
■ 通过高斯函数定义的卷积核
○ 核中心:(0,0)
○ 核大小:3x3
○ 标准方差σ:1.5
○ 计算步骤:
■ 将(x,y)带入公式中,计算出权重值,(权重值代表当前处理像素的影响程度,离中心越近权重越大)
■ 为了保证卷积后图像不变暗,需要对高斯核进行归一化处理(每个权重除以所有权重的和)
请添加图片描述
在这里插入图片描述
● 补充:
○ 参考:GAMES101-L6内容
○ 滤波(Filtering)
■ 滤波就是抹掉特殊频率的东西
■ 不同滤波的效果:
● 高通滤波 = 边界
● 低通滤波 = 模糊
在这里插入图片描述

○ 滤波(Filtering)=卷积(Convolution)=平均(Averaging)
○ 卷积操作的定义
■ ①原始信号的任意一个位置,取其周围的平均
■ ②作用在一个信号上,用一种滤波操作,得到一个结果Result

三、 Bloom算法的应用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
四、Reference
● 图片参考:· https://unsplash.com/·
● 自行拍摄项目参考:· https://github.com/keijiro/KinoBloom· https://github.com/MarcusXie3D/FastBloomForMobiles
● 资料参考:
○ learnopengl:https://learnopengl.com/Advanced-Lighting/Bloom·
○ Unity Shader入门精要:12.5 Bloom效果·
○ https://en.wikipedia.org/wiki/Bloom_(shader_effect)
○ https://en.wikipedia.org/wiki/High_dynamic_range
○ https://zhuanlan.zhihu.com/p/76505536

Shader "Unlit/DS_Bloom"
{
    Properties
    {
        // _MainTex为渲染纹理,变量名固定不能改变
        //模糊结果、阈值、模糊半径的变量名与C#脚本中的对应
        _MainTex ("Texture", 2D) = "white" {}
        _Bloom ("Bloom (RGB)", 2D) = "black" {} //高斯模糊后的结果
		_LuminanceThreshold ("Luminance Threshold", Float) = 0.5 //阈值
		_BlurSize ("Blur Size", Float) = 1.0 //模糊半径
    }
    SubShader
    {
        //用CGINCLUDE和ENDCG
        //Unity会把它们之间的代码插入到每一个pass中,已达到声明一遍,多次使用的目的。
        CGINCLUDE
        #include "UnityCG.cginc"

        //声明属性和C#脚本中用到的变量
        sampler2D _MainTex;
		half4 _MainTex_TexelSize;//纹素大小
		sampler2D _Bloom;
		float _LuminanceThreshold;
		float _BlurSize;

        //########第1个pass使用########
        //输出结构
        struct v2fExtractBright {
			float4 pos : SV_POSITION; 
			half2 uv : TEXCOORD0;
		};
        
        //顶点着色器
        v2fExtractBright vertExtractBright(appdata_img v) {
        	//appdata_img是官方提供的输入结构,只包含图像处理时必须的顶点坐标和uv等变量
			v2fExtractBright o;
			o.pos = UnityObjectToClipPos(v.vertex);
			o.uv = v.texcoord;	 
			return o;
		}
        
        // 明亮度公式
        // 在RGB模式下,像素亮度的计算公式为:L=R*0.30+G*0.59+B*0.11,简称305911公式
		fixed luminance(fixed4 color) {
        	//计算得到像素的亮度值
			return  0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b; 
		}
        
        //片元着色器->提取高亮区域
        fixed4 fragExtractBright(v2fExtractBright i) : SV_Target {
        	fixed4 c = tex2D(_MainTex, i.uv);// 贴图采样
			
			// 调用luminance得到采样后像素的亮度值,再减去阈值
			// 使用clamp函数将结果截取在[0,1]范围内
			//clamp() 函数的作用是把一个值限制在一个上限和下限之间,当这个值超过最小值和最大值的范围时,在最小值和最大值之间选择一个值使用
			fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.0, 1.0);
			// 将val与原贴图采样得到的像素值相乘,得到提取后的亮部区域
			return c * val;
		}

        
        //########第2、3个pass使用########
        //输出结构
        struct v2fBlur {
			float4 pos : SV_POSITION;
        	half2 uv[5]: TEXCOORD0;
			// 此处定义5维数组用来计算5个纹理坐标
        	// 由于卷积核大小为5x5的二维高斯核可以拆分两个大小为5的一维高斯核
        	// uv[0]存储了当前的采样纹理
        	// uv[1][2][3][4]为高斯模糊中对邻域采样时使用的纹理坐标
		};
        
        //顶点着色器->计算竖直方向进行高斯模糊的uv
        v2fBlur vertBlurVertical(appdata_img v) {
			
			v2fBlur o;
			o.pos = UnityObjectToClipPos(v.vertex);//将顶点从模型空间变换到裁剪空间下
			half2 uv = v.texcoord;
			o.uv[0] = uv;
        	//uv[0]就是(0,0)
        	//对竖直方向进行模糊
			//对应到邻域就是下边的情况
        	//uv[1],向上挪动1个单位(0, 1)
        	//uv[2],向下挪动1个单位(0, -1)
        	//uv[3],向上挪动2个单位(0, 2)
        	//uv[3],向下挪动2个单位(0, -2)
        	//最后乘上模糊半径作为参数控制
			o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
			o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
					 
			return o;
		}
        
        //顶点着色器->计算水平方向进行高斯模糊的uv
        v2fBlur vertBlurHorizontal(appdata_img v) {
			v2fBlur o;
			o.pos = UnityObjectToClipPos(v.vertex);
			half2 uv = v.texcoord;
        	o.uv[0] = uv;
        	//uv[0]就是(0,0)
        	//对水平方向进行模糊
			//同理,uv[1]到[4]分别对应(1, 0)、(-1, 0)、(2, 0)、(-2, 0)
			o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
			o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
			o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
			o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
					 
			return o;
		}
        //片元着色器->进行高斯模糊
        fixed4 fragBlur(v2fBlur i) : SV_Target {
			float weight[3] = {0.4026, 0.2442, 0.0545};
        	// 因为二维高斯核具有可分离性,而分离得到的一维高斯核具有对称性
			// 所以只需要在数组存放三个高斯权重即可
			
			fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
        	// 结果值sum初始化为当前的像素值乘以它对应的权重值

        	// 进行卷积运算,根据对称性完成两次循环
			// 第一次循环计算第二个和第三个格子内的结果
			// 第二次循环计算第四个和第五个格子内的结果
			for (int it = 1; it < 3; it++) {
				sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
				sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
			}
			
			return fixed4(sum, 1.0);// 返回滤波后的结果
		}

        
        //########第4个pass使用########
        //输出结构
        struct v2fBloom {
			float4 pos : SV_POSITION; 
			half4 uv : TEXCOORD0;
		};

        //顶点着色器
        v2fBloom vertBloom(appdata_img v) {
			v2fBloom o;
			o.pos = UnityObjectToClipPos (v.vertex);
			
			o.uv.xy = v.texcoord; //xy分量为_MainTex的纹理坐标		
			o.uv.zw = v.texcoord; //zw分量为_Bloom的纹理坐标
			
			// 平台差异化处理
        	//判断y是否小于0,如果是就进行翻转处理
			#if UNITY_UV_STARTS_AT_TOP			
			if (_MainTex_TexelSize.y < 0.0)
				o.uv.w = 1.0 - o.uv.w;
			#endif
			return o; 
		}

        //片元着色器->混合亮部和原图
        fixed4 fragBloom(v2fBloom i) : SV_Target {
		    // 把这两张纹理的采样结果相加即可得到最终效果
			return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);
		}
    	ENDCG
    	
        // 开启深度测试,关闭剔除和深度写入
        ZTest Always 
    	Cull Off 
    	ZWrite Off
        
    	//第一个pass,提取较亮区域
        Pass{
            CGPROGRAM
			#pragma vertex vertExtractBright
			#pragma fragment fragExtractBright
            ENDCG
        }
	    
    	//第二个pass,进行竖直方向高斯模糊
    	Pass{
            CGPROGRAM
			#pragma vertex vertBlurVertical
			#pragma fragment fragBlur
            ENDCG
        }
    	
    	//第三个pass,进行水平方向高斯模糊
    	Pass{
            CGPROGRAM
			#pragma vertex vertBlurHorizontal
			#pragma fragment fragBlur
            ENDCG
        }
    	
    	//第四个pass,混合高亮区域和原图
    	Pass{
            CGPROGRAM
			#pragma vertex vertBloom
			#pragma fragment fragBloom
            ENDCG
        }
    }
	FallBack Off
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值