屏幕后处理 高斯模糊和Bloom
创始人
2025-05-31 09:14:35
0

高斯模糊

滤波:在图像处理中,通过滤波强调图片的一些特征或去除图片中一些不重要的部分,高斯模糊就是一种滤波方式,它的作用就是让图片平滑的模糊,滤波是一个邻域操作算子,利用给定像素周围的像素值决定此像素的最终输出值

高斯模糊使用高斯核进行卷积运算,也就是对每个像素和其周围像素进行加权平均

在这里插入图片描述

图中左侧是一个5×5的高斯核,高斯核中所有的权重值相加为1,且中心权重值大,边缘权重值小,也就是距离越近的像素影响越大

使用一个NxN的高斯核对图像进行卷积滤波,就需要N×N×W×H(W和H是图像的宽和高)次纹理采样,我们可以把二维的高斯核拆成两个一维的高斯核先后对图像进行滤波,得到的结果和使用二维高斯核是一样的,但采样次数只需要2×N×W×H

在这里插入图片描述

这两个一维的高斯核是对称的,其中有很多重复的权重,实际只使用了 0.4026,0.2442, 0.0545 这三个权重

实现高斯模糊还需要设置三个参数

  • iterations(迭代次数):进行几次高斯滤波,次数越多,图像越模糊
  • blurSize(模糊范围):blurSize是用来控制高斯滤波器的偏移量的,也就是每个采样点之间的距离。blurSize越大,采样点越分散,模糊范围越大。但是,blurSize过大也会造成虚影的问题
  • downSample(降采样):缩小图像来提高性能,比如1024 x 1024的的图像,降采样为2,图像就被缩小为512 x 512,降采样设置太大可能使图像像素化

代码实现,创建GaussianBlur脚本挂在相机上,用于调整参数

using UnityEngine;
public class GaussianBlur : PostEffectsBase 
{public Shader gaussianBlurShader;private Material gaussianBlurMaterial = null;public Material material {  get {gaussianBlurMaterial = CheckShaderAndCreateMaterial(gaussianBlurShader, gaussianBlurMaterial);return gaussianBlurMaterial;}  }// 降采样[Range(1, 8)]public int downSample = 2;// 迭代次数[Range(0, 4)]public int iterations = 3;// 模糊范围[Range(0.1f, 3.0f)]public float blurSize = 0.6f;void OnRenderImage (RenderTexture src, RenderTexture dest){if (material == null) {Graphics.Blit(src, dest);return;}int rtW = src.width / downSample;int rtH = src.height / downSample;// 创建一个临时的渲染纹理并赋值给buffer0RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);buffer0.filterMode = FilterMode.Bilinear;// 把src中的图像缩放后存储到buffer0中Graphics.Blit(src, buffer0);for (int i = 0; i < iterations; i++) {material.SetFloat("_BlurSize", 1.0f + i * blurSize);RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);// 调用第一个Pass时,输入是buffer0,输出是buffer1,buffer0纹理会被传递给Shader中名为_MainTex的纹理属性Graphics.Blit(buffer0, buffer1, material, 0);// 先把buffer0释放,再把buffer0指向buffer1,重新分配buffer1RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);// 调用第二个PassGraphics.Blit(buffer0, buffer1, material, 1);RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;}Graphics.Blit(buffer0, dest);RenderTexture.ReleaseTemporary(buffer0);}
}

shader实现,两个Pass使用竖直方向和水平方向的一维高斯核对图像进行滤波,得到最终的目标图像

Shader "MyCustom/GaussianBlur"
{Properties {_MainTex ("Base (RGB)", 2D) = "white" {}_BlurSize ("Blur Size", Float) = 1.0}SubShader {// CGINCLUDE类似于C++中头文件的功能。由于高斯模糊需要定义两个Pass,但它们使用的片元着色器代码// 是完全相同的,使用CGINCLUDE可以避免我们编写两个完全一样的frag函数。CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex;  half4 _MainTex_TexelSize;float _BlurSize;struct v2f{float4 pos : SV_POSITION;half2 uv[5]: TEXCOORD0;};// appdata_img 只包含顶点坐标和uvv2f vertBlurVertical(appdata_img v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;// 当前的uvo.uv[0] = uv;// 上下1个单位的uv,_BlurSize控制采样距离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;// 上下2个单位的uvo.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;}v2f vertBlurHorizontal(appdata_img v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;o.uv[0] = uv;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(v2f i) : SV_Target{float weight[3] = {0.4026, 0.2442, 0.0545};// 当前的uv使用权重weight[0]fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];for (int it = 1; it < 3; it++){//上下1个单位(或左右1个单位)的uv使用权重weight[1]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);}ENDCGZTest AlwaysCull OffZWrite OffPass {//定义名字,可以在其他shader中使用该pass,必须大写NAME "GAUSSIAN_BLUR_VERTICAL"CGPROGRAM#pragma vertex vertBlurVertical  #pragma fragment fragBlurENDCG  }Pass{  NAME "GAUSSIAN_BLUR_HORIZONTAL"CGPROGRAM#pragma vertex vertBlurHorizontal  #pragma fragment fragBlurENDCG}} FallBack "Diffuse"
}

在这里插入图片描述
脚本上引用shader,实际效果
在这里插入图片描述

在这里插入图片描述
用Frame Debug查看渲染过程,第一个和最后一个Pass是用来拷贝贴图的,其他的都是高斯模糊用到的两个Pass,迭代次数加1,就会多两个Pass,这是迭代次数为3的情况

Bloom(辉光效果)

Bloom效果是建立在高斯模糊的基础上的,它让画面中较亮的区域 “扩散” 到周围的区域中,造成一种朦胧的效果
实现过程:

  1. 根据一个阈值提取出图像中的较亮区域,把它们存储在一张RT中
  2. 利用高斯模糊对这张渲染纹理进行模糊处理,模拟光线扩散的效果
  3. 将这张RT和原图像进行混合,得到最终的效果

在相机上添加脚本Bloom,内容和高斯模糊差不多,增加了一个参数用于提取较亮区域

using UnityEngine;
public class Bloom : PostEffectsBase
{public Shader bloomShader;private Material bloomMaterial = null;public Material material{  get{bloomMaterial = CheckShaderAndCreateMaterial(bloomShader, bloomMaterial);return bloomMaterial;}  }// 降采样[Range(1, 8)]public int downSample = 2;// 迭代次数[Range(0, 4)]public int iterations = 3;// 模糊范围[Range(0.1f, 3.0f)]public float blurSize = 0.6f;// 图像的亮度值一般不会超过1。但如果我们开启了HDR,硬件会允许我们把// 颜色值存储在一个更高精度范围的缓冲中,此时像素的亮度值可能会超过1[Range(0.0f, 4.0f)]public float luminanceThreshold = 0.6f;void OnRenderImage (RenderTexture src, RenderTexture dest){if (material == null){Graphics.Blit(src, dest);return;}material.SetFloat("_LuminanceThreshold", luminanceThreshold);int rtW = src.width / downSample;int rtH = src.height / downSample;RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);buffer0.filterMode = FilterMode.Bilinear;// Pass0提取图像中的较亮区域,提取得到的较亮区域将存储在buffer0中Graphics.Blit(src, buffer0, material, 0);// Pass1,Pass2利用高斯模糊对这张渲染纹理进行模糊处理,模拟光线扩散的效果for (int i = 0; i < iterations; i++){material.SetFloat("_BlurSize", 1.0f + i * blurSize);RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);// 竖直方向对图像进行滤波Graphics.Blit(buffer0, buffer1, material, 1);RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);// 水平方向对图像进行滤波Graphics.Blit(buffer0, buffer1, material, 2);RenderTexture.ReleaseTemporary(buffer0);buffer0 = buffer1;}material.SetTexture ("_Bloom", buffer0);// Pass3将buffer0和原图src进行混合Graphics.Blit(src, dest, material, 3);RenderTexture.ReleaseTemporary(buffer0);}
}

shader实现,高斯模糊部分直接引用上面的Pass

Shader "MyCustom/Bloom"
{Properties{_MainTex ("Base (RGB)", 2D) = "white" {}_Bloom ("Bloom (RGB)", 2D) = "black" {}	 //高斯模糊后的较亮区域_LuminanceThreshold ("Luminance Threshold", Float) = 0.5_BlurSize ("Blur Size", Float) = 1.0}SubShader{CGINCLUDE#include "UnityCG.cginc"sampler2D _MainTex;half4 _MainTex_TexelSize;sampler2D _Bloom;float _LuminanceThreshold;float _BlurSize;struct v2f{float4 pos : SV_POSITION; half2 uv : TEXCOORD0;};	v2f vertExtractBright(appdata_img v){v2f 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 fragExtractBright(v2f i) : SV_Target{//提取亮部区域fixed4 c = tex2D(_MainTex, i.uv);fixed val = clamp(luminance(c) - _LuminanceThreshold, 0.0, 1.0);return c * val;}struct v2fBloom{float4 pos : SV_POSITION; half4 uv : TEXCOORD0;};v2fBloom vertBloom(appdata_img v){v2fBloom o;o.pos = UnityObjectToClipPos (v.vertex);//xy分量对应了_MainTex,即原图像的纹理坐标。而它的zw分量是_Bloomo.uv.xy = v.texcoord;		o.uv.zw = v.texcoord;//对这个纹理坐标进行平台差异化处理#if UNITY_UV_STARTS_AT_TOP			if (_MainTex_TexelSize.y < 0.0)o.uv.w = 1.0 - o.uv.w;#endifreturn o; }fixed4 fragBloom(v2fBloom i) : SV_Target{return tex2D(_MainTex, i.uv.xy) + tex2D(_Bloom, i.uv.zw);} ENDCGZTest AlwaysCull OffZWrite OffPass{CGPROGRAM#pragma vertex vertExtractBright#pragma fragment fragExtractBrightENDCG}UsePass "MyCustom/GaussianBlur/GAUSSIAN_BLUR_VERTICAL"UsePass "MyCustom/GaussianBlur/GAUSSIAN_BLUR_HORIZONTAL"Pass{CGPROGRAM#pragma vertex vertBloom#pragma fragment fragBloomENDCG}}FallBack Off
}

实际效果
在这里插入图片描述
用Frame Debug查看渲染过程
在这里插入图片描述
这个Pass0处理后的图像,保留了较亮区域,较暗区域为纯黑色

在这里插入图片描述
高斯模糊多次迭代后,最后和原图叠加就生成最终效果

参考

《Unity Shader入门精要》

相关内容

热门资讯

“好梦不长”的意思 “好梦不长”的意思 成语拼音: [hǎo mèng bù cháng] ...
迥然不同成语 迥然不同成语迥然不同成语1  【成语】:迥然不同  【拼音】:jiǒng rán bù tóng  ...
“神眉鬼眼”的意思 “神眉鬼眼”的意思 成语拼音: [shén méi guǐ yǎn] ...
人物动作神态描写成语 人物动作神态描写成语  在书写人物作文的时候,大家知道怎么用书写人物的神态吗?以下是小编为大家整理好...
软件设计师教程(十三)计算机系... 软件设计师教程 软件设计师教程(一)计算机系统知识-计算机系统基础知识 ...
DWF文件怎么用CAD打开?D... DWF是一种开放、安全的文件格式,它可以将丰富的设计数据高效率地分发给需要查看、评审或...
“阵马风樯”的意思 “阵马风樯”的意思 成语拼音: [zhèn mǎ fēng qiáng] ...
“高垒深堑”的意思 “高垒深堑”的意思 成语拼音: [gāo lěi shēn qiàn] ...
“冬温夏凊”的意思 “冬温夏凊”的意思 成语拼音: [dōng wēn xià jìng] ...
形容孝的成语 形容孝的成语  事其亲者,不择地而安之,孝之至也。以下是形容孝的成语,欢迎阅读。  彩衣娱亲:传说春...
软件的开发工具包(SDK)与集... 本文重点论述软件的开发工具包(SDK)与集成开发环境(ID...
机器学习算法--朴素贝叶斯(N... 1. 朴素贝叶斯(Naive Bayes) 朴素贝叶斯的介绍 朴素贝叶斯算法(Naiv...
描写大雪的词语 描写大雪的词语  词语是词和短语的合称,包括词(含单词、合成词)和词组(又称短语),组成语句文章的最...
司马昭之心成语意思 司马昭之心成语意思  【成语】:司马昭之心  【拼音】:sī mǎ zhāo zhī xīn  【简...
“大处着眼,小处着手”的意思 “大处着眼,小处着手”的意思 成语拼音: [dà chù zhuó yǎn,xiǎo c...
“退食自公”的意思 “退食自公”的意思 成语拼音: [tuì shí zì gōng] ...
FLEXPART拉格朗日粒子扩... 查看原文>>>https://mp.weixin.qq.com/s?__biz=MzAxNz...
u盘安装win10系统2 好不容易完成系统安装u盘的制作,心想接下来应该会比较顺利吧,结果被第一个...
描写家禽的词语有哪些 描写家禽的词语有哪些  二字词:  划行  油亮  雪白  银灰  飞翔  奋飞  戏水  俯冲  ...
“怏怏不悦”的意思 “怏怏不悦”的意思 成语拼音: [yàng yàng bù yuè] ...