【TA100】ブルームアルゴリズム

1. ブルームアルゴリズムとは何ですか?

1. まず、ブルーム効果がどのようなものかを見てみましょう。

ここに画像の説明を挿入します

2. ブルームとは何ですか?

● ブルーム (グローとも呼ばれる) は、一般的な画面効果です。
● カメラの画像効果をシミュレートし、写真内の明るい領域を周囲の領域に「拡散」させ、かすんだ効果を作成します。 ● オブジェクトを作成できます
。明るい効果
● ハロー効果が得られます
ここに画像の説明を挿入します

3. Bloomの実装原理

①ブルーム実装原理
● 実装アイデア:
○ 1. 元画像の明るい部分を抽出(閾値を使用)
○ 2. 画像をぼかす
○ 3. 元画像と混合・重ね合わせる
ここに画像の説明を挿入します

● / Bloom については、HDR と LDR の授業でも出てきましたので、その時に作成したフローチャートを参考にそのまま抜粋しました:
○
② 前提知識1:HDR と LDR
● HDR と LDR は、それぞれ High Dynamic Range と Low Dynamic の略です範囲
● LDR
○ jpg、png 形式の写真
○ RGB in [0,1]

● HDR
○ HDR、EXR 形式の写真
○ は 1 を超える場合があります。
● 自然界の明るさの差は非常に大きいため (たとえば、ろうそくの光の強さは約 15 ですが、太陽の強さは約 10 ワットです)、多くの効果が得られます。 LDRだけで表現しきれていない

③ 前提知識 2: ガウスぼかし
● 画像のぼかしを実現する方法
● ガウスぼかし:
○ ガウス カーネルを使用して畳み込み演算を実行し、ぼやけた画像を取得します。
ここに画像の説明を挿入します
ガウス カーネル
○ ガウス カーネル:
■ ガウス関数で定義されたコンボリューション カーネル
○ カーネル センター: (0 ,0)
○ カーネル サイズ: 3x3
○ 標準偏差 σ: 1.5
○ 計算手順:
■ (x, y) を式に代入し、重み値を計算します (重み値は、現在処理されているピクセルの影響度を表します。中心に近いほど重みが大きくなります)
■畳み込み後に画像が暗くならないようにするには、ガウスカーネルを正規化する必要があります(各重みをすべての重みの合計で割ります) ●補足:○ 参考
画像の説明を追加してください
ここに画像の説明を挿入します

GAMES101 -L6 内容
○ フィルタリング
■ フィルタリングとは、特殊な周波数のものを消去すること
■ さまざまなフィルタの効果:
● ハイパス フィルタリング = 境界線
● ローパス フィルタリング = ぼかし
ここに画像の説明を挿入します

○ フィルタリング = 畳み込み = 平均化
○ 畳み込み演算の定義
■ ① 元の信号の任意の位置で、その周囲の平均を取る
■ ② 信号に作用し、フィルタ演算を使用し、結果を取得する結果

3. ブルームアルゴリズムの適用

ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
ここに画像の説明を挿入します
4. 参考
● 画像参考: ・ https://unsplash.com/
・ ● 自撮りプロジェクト参考: ・ https://github.com/keijima/KinoBloom ・ https://github.com/MarcusXie3D/FastBloomForMobiles
● データ参考:
○ learnopengl: https://learnopengl.com/Advanced-Lighting/Bloom ·
○ Unity Shader Getting Started Essentials: 12.5 Bloom Effect ·
○ 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
}

おすすめ

転載: blog.csdn.net/weixin_45810196/article/details/131378036