方框模糊、高斯模糊和双重模糊的实现原理和方法

方框模糊(Box Blur)

我们知道,位图其实都是由无数个像素点组合而成的,每个像素点都带有颜色值,方框模糊的基本原理就是:遍历每一个像素点,对像素点进行重新赋值,采样方法通常是取临近像素点颜色值之和的均值,因此方框模糊又被称为均值模糊。

最简单的一种采样方法就是2*2采样,取目标像素点周围四个角的像素值,然后求均值。

举个最简单的例子!把下面左图理解为一张图片,每个色块代表一个像素点。


使用2*2采样方法遍历每一个像素点,那么最后方框模糊的效果就像右图这样。以左上角的像素块为例,它的RGB色值为(75,75,194),因为是边缘色块,它只有右下角色值为RGB(153,75,194)一个色块,那模糊后的色值计算方法(四舍五入取整数值)为:

R = 153 / 4 = 67

G = 75 / 4 = 18

B = 194 / 4 = 48

它模糊后的最终色值为RGB(67,18,48),也就是右侧左上方的暗紫色色块。

其他像素点的模糊算法也以此类推。

具体用Unity Shader 实现如下:

float _BlurOffset;

            half4 frag (v2f_img i) : SV_Target
            {
                half4 s = 0;
                half4 d =  _MainTex_TexelSize.xyxy * half4(-1,-1,1,1) * _BlurOffset;
                // 采样右上角的值
                s += tex2D(_MainTex, i.uv + d.yz);
                // 采样右下角的值
                s += tex2D(_MainTex, i.uv + d.xy);
                // 采样左下角的值
                s += tex2D(_MainTex, i.uv + d.zy);
                // 采样左上角的值
                s += tex2D(_MainTex, i.uv + d.zw);
                s *= 0.25;
                return s;
            }

2*2 采样方法虽然计算简单,但它的模糊效果并不理想,单次迭代模糊过渡并不顺滑,很容易出现色块,就像下面一样。


要解决这个问题,需要经过多次迭代才能达到比较好的效果。

既然22采样方法不太理想,那么可以考虑采样33的采样方法,也就是采样目标像素周围9个像素点的色值(包括自身)。采样方法可以根据实际项目需要灵活调整,比如说44、55的采样方法,这里就不再一一列举了,只给出3*3采样方法的简单示例:

还是以左上方色块为例,使用3*3采样方法,他的邻近像素色块只有四块,那么方框模糊后的色值计算方法(四舍五入取整数值)为:

R = (75+75+75+153)/ 9 = 42

G = (75+117+194+75)/9 = 51

B = (194+194+125+194)/9 = 79

它模糊后的最终色值为RGB(67,18,48),也就是右侧左上方的灰紫色色块。

其他像素点的模糊算法也以此类推。

具体用Unity Shader 实现如下:

half4 frag_BoxFilter_9Tap (v2f_img i) : SV_Target
            {
                half4 s = 0;
                half4 d =  _MainTex_TexelSize.xyxy * half4(-1,-1,1,1) * _BlurOffset;
                s = tex2D(_MainTex, i.uv);
                // 采样右上角的值 -1 1
                s += tex2D(_MainTex, i.uv + d.yz);
                // 采样右下角的值 -1 -1
                s += tex2D(_MainTex, i.uv + d.xy);
                // 采样左下角的值 1 -1
                s += tex2D(_MainTex, i.uv + d.zy);
                // 采样左上角的值 1 1
                s += tex2D(_MainTex, i.uv + d.zw);

                // 采样上下左右四个值
                s+=  tex2D(_MainTex, i.uv + half2(0.0,d.w)); // 0,1
                s+=  tex2D(_MainTex, i.uv + half2(0.0,d.y)); // 0,-1
                s+=  tex2D(_MainTex, i.uv + half2(d.z,0.0)); // 1,0
                s+=  tex2D(_MainTex, i.uv + half2(d.x,0.0)); // -1,0
  
                s = s/9.0;
                return s;
            }

在其他参数相同的情况下,33采样方法模糊后的效果,会比22采样方法过渡更自然,更不容易产生色块,颜色细节也会更加丰富。

高斯模糊(Gaussian Blur)

理解了方框模糊,再来解释高斯模糊就更加容易了。

首先,高斯模糊是采用5*5的采样方法,也就是需要采样目标像素点周围25个像素点的色值(包括目标像素)。

但区别于方框模糊,高斯模糊采样的每个像素点需要再乘以一个权重值,越靠近目标值,权重值越大。


如果计算每一个像素点高斯模糊后的值,工作量将会变得特别大。有一种方法能将计算变得非常简单,那就是先对像素进行水平采样,然后再垂直拆样。具体采样方法如下:

使用unity shader 实现代码如下:

// 高斯模糊 水平采样
            half4 frag_HorizontalBlur (v2f_img i) : SV_Target
            {
                half2 uv1 = i.uv + _BlurOffset.xy * half2(1,0) * -2.0;
                half2 uv2 = i.uv + _BlurOffset.xy * half2(1,0) * -1.0;
                half2 uv3 = i.uv;
                half2 uv4 = i.uv + _BlurOffset.xy * half2(1,0) * 1.0;
                half2 uv5 = i.uv + _BlurOffset.xy * half2(1,0) * 2.0;

                half4 s = 0;
                s += tex2D(_MainTex, uv1) * 0.05;
                s += tex2D(_MainTex, uv2) * 0.25;
                s += tex2D(_MainTex, uv3) * 0.40;
                s += tex2D(_MainTex, uv4) * 0.25;
                s += tex2D(_MainTex, uv5) * 0.05;

                return s;
            }

            // 高斯模糊 垂直采样
             half4 frag_VerticalBlur (v2f_img i) : SV_Target
            {
                half2 uv1 = i.uv + _BlurOffset.xy * half2(0,1) * -2.0;
                half2 uv2 = i.uv + _BlurOffset.xy * half2(0,1) * -1.0;
                half2 uv3 = i.uv;
                half2 uv4 = i.uv + _BlurOffset.xy * half2(0,1) * 1.0;
                half2 uv5 = i.uv + _BlurOffset.xy * half2(0,1) * 2.0;

                half4 s = 0;
                s += tex2D(_MainTex, uv1) * 0.05;
                s += tex2D(_MainTex, uv2) * 0.25;
                s += tex2D(_MainTex, uv3) * 0.40;
                s += tex2D(_MainTex, uv4) * 0.25;
                s += tex2D(_MainTex, uv5) * 0.05;

                return s;
            }

实现的效果如下:

相比之下,高斯模糊的过渡效果会比方框模糊更加平滑自然。

双重模糊(Dual Blur)

严格来说,双重模糊并不是一种模糊算法,而是一种模糊的优化方法,它可以应用在方框模糊、高斯模糊或者其他模糊算法之上。它的实现思路在于:在原有模糊方法的基础上,采用降采样、升采样的方法进行迭代,从而达到模糊的优化。

那么什么叫降采样?什么叫升采样呢?

先看以下一段代码:

private void OnRenderImage(RenderTexture source, RenderTexture destination){
        int width = source.width;
        int height = source.height;
        RenderTexture RT1 = RenderTexture.GetTemporary(width, height);
        RenderTexture RT2 = RenderTexture.GetTemporary(width, height);
                Graphics.Blit(RT1, RT2);
}

这是一段最简单采样渲染图片的代码,新建了两块画布,画布的尺寸为屏幕的宽、高。

在采样的过程中,如果画布的宽高越来越小,采样的速度越快,画质会变模糊,渲染速度变快,这个过程就叫做降采样。

与此相反,画布的宽高越来越大,采样的速度变慢,但画质渲染会更加精细,渲染速度变慢,这样过程就叫做升采样。

双重模糊就是通过不断迭代,先降采样然后再升采样的方法,来达到优化模糊效果的。

以高斯模糊为例,它使用双重模糊的代码如下:

Graphics.Blit(source, RT1);
        // 设置绑定的材质参数
        material.SetVector("_BlurOffset", new Vector4(_BlurRadius / width, _BlurRadius / height,0,0));
        // 降采样
        for (int i = 0; i < _Iteration; i++) {
            RenderTexture.ReleaseTemporary(RT2);
            width = width / 2;
            height = height / 2;
            RT2= RenderTexture.GetTemporary(width, height);
            Graphics.Blit(RT1, RT2, material, 0);

            RenderTexture.ReleaseTemporary(RT1);
            width = width / 2;
            height = height / 2;
            RT1 = RenderTexture.GetTemporary(width, height);
            Graphics.Blit(RT2, RT1, material, 0);
        }
        // 升采样
        for (int i = 0; i < _Iteration; i++)
        {
            RenderTexture.ReleaseTemporary(RT2);
            width = width * 2;
            height = height * 2;
            RT2 = RenderTexture.GetTemporary(width, height);
            Graphics.Blit(RT1, RT2, material, 0);

            RenderTexture.ReleaseTemporary(RT1);
            width = width * 2;
            height = height * 2;
            RT1 = RenderTexture.GetTemporary(width, height);
            Graphics.Blit(RT2, RT1, material, 0);
        }

        Graphics.Blit(RT2, destination);

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容