【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

这篇具有很好参考价值的文章主要介绍了【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

发现之前学习记录的太过详细,导致整理的过程占用太长的时间了,这篇之后博客重要的是掌握实现过程,关于基础的理论会更多的放上别人写得更好的文章。


参考:【Unity Shader编程】之十五 屏幕高斯模糊(Gaussian Blur)后期特效的实现

1 基础概念

1.1 模糊

高斯模糊只是各种模糊方式中的一种。模糊,从图像处理角度可以理解成每个像素都取周围像素的均值,数值上会呈现一种“平滑”的效果,图形上就是中心像素失去细节,达到模糊效果!显然,既然是取均值,那自然这个均值的范围越大,模糊效果越厉害。

那么问题就来了——卷积核内每个网格内的权重该怎么分配? 

1.2 均值模糊

是直接均匀分配吗?如果周围像素取均值权重都一样,也就是取算数平均值模糊,这种模糊方式叫做均值模糊,均值模糊的卷积核很简单:(下图来自忘了哪一篇的参考文章)

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

1.3 什么是降采样

哦对了!在实践均值/高斯模糊之前,还需要知道一个概念——降采样(DownSample)。字面理解其实就是降低采样率,一般会给个降采样系数。例如降采样系数取2的话,就是在源图像上每行每列每隔2个像素组成一幅图像。降采样系数越大,我们需要处理的像素就少了,那么图片就会越模糊,但如果太大了,得到的结果可能直接像素化了。

1.4 什么是高斯模糊

什么是高斯模糊?——使用正态分布作为卷积权重表的像素平滑方法。

正态分布

这是正态分布公式:

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

这是正态分布图像:

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

 正态分布我们再熟悉不过了, 数学中常常被我们拿来处理各种概率计算问题,而且正态分布计算非常简单,可以快速得出结果,非要详细学习的话,推荐看看:正态分布在日常生活中的应用(一) - 知乎 (zhihu.com),这里不再赘述这些基础知识。

高斯函数

上面给出的正态分布,都是一维的关系,而我们想基于图像做正态分布,那就需要二维的正态分布。正态分布密度函数叫做高斯函数(Gaussian function),高斯函数的二维形式可以根据一维形式推导出来,结果如下:

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

有了这个函数我们能干嘛?还记得吗,第1节中我们探讨了“权重”的问题,而这个函数刚好能帮我们解决权重问题,因此接下来我们需要计算权重矩阵&模糊值,这一部分直接参考真正搞懂均值模糊、中值模糊、高斯模糊、双边模糊中的内容就行。而经过一系列操作后获得的权重集合,就可以当作进行高斯模糊卷积操作的卷积核。

拆分高斯卷积核

本身高斯函数就是正态分布的密度函数,而二维的正态分布函数本身就可以拆分成x、y两个轴向上的一维正态分布函数, 所以将卷积核拆分成两个一维的卷积核是可行的。 详细解释可参考:高斯模糊用到的二维高斯卷积核,为什么可以拆成两个一维的高斯卷积核?

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

行,理论知识点到为止,这里就先简单实践一下均值模糊和高斯模糊的效果:

2 实现均值模糊

实现模糊效果,一般会给用户提供的可以调整的参数有

  • 迭代次数
  • 模糊范围
  • 降采样系数

实现均值模糊很简单,我取了3X3的卷积核,C#代码和Shader代码如下:

2.1 C#脚本 

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

[ExecuteInEditMode]
public class BoxBlur : MonoBehaviour
{
    public Shader blurShader;
    public Material blurMaterial;
    [Header("迭代次数")]
    [Range(0, 4)]
    public int blurIterations = 1;

    [Header("模糊范围")]
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.2f;

    [Header("降采样系数")]
    [Range(1, 8)]
    public int downSample = 2;

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(blurMaterial != null)
        {
            int rtW = source.width / downSample;
            int rtH = source.height / downSample;
            //定义缓存rt
            RenderTexture rt = RenderTexture.GetTemporary(rtW, rtH, 0);

            //把source缩放后,存到了rt上
            Graphics.Blit(source, rt, blurMaterial);

            //开始迭代模糊
            for (int i = 0; i < blurIterations; i++)
            {
                blurMaterial.SetFloat("_BlurSize", blurSpread);
                Graphics.Blit(rt, source, blurMaterial);
                Graphics.Blit(source, rt, blurMaterial);
            }
            //输出结果
            Graphics.Blit (rt, destination, blurMaterial);
            //释放缓存
            RenderTexture.ReleaseTemporary(rt);

        }
        else
        {
            Debug.Log("Please input your Material");
            Graphics.Blit(source, destination);
        }
    }
}

2.2 Shader代码

Shader "Unity Shaders Book/Chapter 12/BoxBlur"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        ZTest Always
        Cull Off
        ZWrite Off
        Pass {
            CGPROGRAM
            
            #pragma vertex vert
            #pragma fragment BoxBlur
            #include "UnityCG.cginc"

            //properties
            sampler2D _MainTex;
            half4 _MainTex_TexelSize;
            float _BlurSize;

            struct v2f {
                float4 pos : SV_POSITION;
                half2 uv[9] : TEXCOORD0;  
            };

            v2f vert(appdata_img v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                half2 uv = v.texcoord;

                //加入_BlurSize控制采样距离
                o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1) * _BlurSize;
                o.uv[1] = uv + _MainTex_TexelSize.xy * half2(0, -1) * _BlurSize;
                o.uv[2] = uv + _MainTex_TexelSize.xy * half2(1, -1) * _BlurSize;
                o.uv[3] = uv + _MainTex_TexelSize.xy * half2(-1, 0) * _BlurSize;
                o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0) * _BlurSize;
                o.uv[5] = uv + _MainTex_TexelSize.xy * half2(1, 0) * _BlurSize;
                o.uv[6] = uv + _MainTex_TexelSize.xy * half2(-1, 1) * _BlurSize;
                o.uv[7] = uv + _MainTex_TexelSize.xy * half2(0, 1) * _BlurSize;
                o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1) * _BlurSize;

                return o;
            }

            fixed3 BoxBlur(v2f i) : SV_Target {
                fixed4 color = fixed4(0, 0, 0, 0);
                for(int j=0;j<9;j++){
                    color +=tex2D(_MainTex, i.uv[j]);
                }
                return color/9.0;
            }
            ENDCG
        }
    }
}

2.3 呈现效果

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊

3 实现高斯模糊

没啥好说的,就是用了两个Pass分别做了水平和竖直向的高斯模糊处理。 

3.1 C#脚本

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

[ExecuteInEditMode]
public class GaussianBlur : MonoBehaviour
{
    public Shader blurShader;
    public Material blurMaterial;
    [Header("迭代次数")]
    [Range(0, 4)]
    public int  blurIterations = 1;

    [Header("模糊范围")]
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.2f;

    [Header("降采样系数")]
    [Range(1, 8)]
    public int downSample = 2;

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if(blurMaterial != null)
        {
            int rtW = source.width / downSample;
            int rtH = source.height / downSample;
            //定义缓存rt
            RenderTexture rt0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            rt0.filterMode = FilterMode.Bilinear;  //设置滤波模式

            //把source缩放后,存到了rt0上
            Graphics.Blit(source, rt0, blurMaterial);

            //开始迭代模糊
            for (int i = 0; i < blurIterations; i++)
            {
                blurMaterial.SetFloat("_BlurSize", blurSpread);
                RenderTexture rt1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                //第一个Pass
                Graphics.Blit(rt0, rt1, blurMaterial, 0);
                RenderTexture.ReleaseTemporary(rt0);  //把rt0释放
                rt0 = rt1;  //把第一个Pass的结果给rt0
                rt1 = RenderTexture.GetTemporary(rtW, rtH, 0);  //创建个新的rt1

                //第二个Pass
                Graphics.Blit(rt0, rt1, blurMaterial, 1);
                RenderTexture.ReleaseTemporary(rt0);
                rt0 = rt1; //继续进行下一个迭代的(如果有的话)
            }

            //输出结果
            Graphics.Blit (rt0, destination, blurMaterial);
            //释放缓存
            RenderTexture.ReleaseTemporary(rt0);

        }
        else
        {
            Debug.Log("Please input your Material");
            Graphics.Blit(source, destination);
        }
    }
}

3.2 Shader代码

Shader "Unity Shaders Book/Chapter 12/GaussianBlur"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        //CGINCLUDE--ENDCG中定义的可以定义一系列Pass中会用到的函数,相当于一个头文件的作用:
        CGINCLUDE

        #include "UnityCG.cginc"

        sampler2D _MainTex;
        half4 _MainTex_TexelSize;
        float _BlurSize;

        struct v2f {
            float4 pos : SV_POSITION;
            half2 uv[5] : TEXCOORD0;  
        };

        //水平
        v2f vertBlurVertical(appdata_img v) {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            half2 uv = v.texcoord;

            o.uv[0] = uv;
            o.uv[1] = uv + float2(0.0f, _MainTex_TexelSize.y * 1.0 * _BlurSize);
            o.uv[2] = uv - float2(0.0f, _MainTex_TexelSize.y * 1.0 * _BlurSize);
            o.uv[3] = uv + float2(0.0f, _MainTex_TexelSize.y * 2.0 * _BlurSize);
            o.uv[4] = uv - float2(0.0f, _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 * _BlurSize, 0.0f);
            o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0 * _BlurSize, 0.0f);
            o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0 * _BlurSize, 0.0f);
            o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0 * _BlurSize, 0.0f);

            return o;
        }

        fixed4 GaussianBlur(v2f i) : SV_Target {
            float weight[3] = {0.4026, 0.2442, 0.0545};
            fixed3 color = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
            
            for(int j=1;j<3;j++) {
                color += tex2D(_MainTex, i.uv[2*j-1]).rgb * weight[j];
                color += tex2D(_MainTex, i.uv[2*j]).rgb * weight[j];
            }
            return fixed4(color, 1.0);
        }
        ENDCG

        ZTest Always
        Cull Off
        ZWrite Off

        //水平的Pass
        Pass {
            NAME "GAUSSIANBLUR_BLUR_VERTICAL"
            CGPROGRAM
            
            #pragma vertex vertBlurVertical
            #pragma fragment GaussianBlur

            ENDCG
        }

        //竖直
        Pass {
            NAME "GAUSSIANBLUR_BLUR_HORIZONTAL"
            CGPROGRAM
            
            #pragma vertex vertBlurHorizontal
            #pragma fragment GaussianBlur

            ENDCG
        }
    }
    FallBack  Off
}

3.3 呈现效果

【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊


感觉均值模糊和高斯模糊看上去区别不是很明显,可能是我对比的问题~当然,细说的话高斯模糊变化的肯定更平滑,因为均值是直接暴力求均值的,忽略了连续性。文章来源地址https://www.toymoban.com/news/detail-400151.html

到了这里,关于【Unity Shader】屏幕后处理3.0:均值模糊和高斯模糊的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图像处理:推导五种滤波算法(均值、中值、高斯、双边、引导)

    目录 概论 算法原理 1、均值滤波 2、中值滤波 3、高斯滤波 4、双边滤波 5、引导滤波  手写代码 Opencv代码实现  最后的总结 参考文章         本来打算是分开推导的,但我觉得还是整个合集吧,避免有水文的嫌疑,那么因为学习的需要,会涉及到图像的滤波处理,我汇总

    2024年02月07日
    浏览(48)
  • 【Unity3D】高斯模糊特效

    1 高斯模糊原理         边缘检测特效中使用了卷积运算进行了边缘检测,本文实现的高斯模糊特效同样使用了卷积运算,关于卷积核和卷积运算的概念,读者可以参考边缘检测特效。         本文完整资源见→Unity3D高斯模糊特效。         我们将用于模糊处理的卷积

    2024年02月07日
    浏览(47)
  • 图像处理中调用matlab自带均值滤波、高斯滤波和中值滤波函数的案例以及自编均值滤波的案例。

    @[TOC](利用matlab自带均值滤波器的代码,分别对一幅图像实现3*3,5*5,7*7,9*9的均值滤波,并对实验结果进行分析。) @[TOC](分别给干净图像添加高斯和椒盐噪声,然后进行均值滤波、高斯滤波和中值滤波,并对实现结果进行分析。) @[TOC](自编均值滤波器对一幅图像实现填充后,

    2024年02月11日
    浏览(41)
  • 高斯模糊与图像处理(Gaussian Blur)

    高斯模糊在图像处理中的用途及其广泛,除了常规的模糊效果外,还可用于图像金字塔分解、反走样、高低频分解、噪声压制、发光效果等等等等。正因为高斯模糊太基础,应用太广泛,所以需要尽可能深入认识这个能力,避免在实际应用中无意采坑。 G ( x ) = 1 2 π σ e −

    2024年02月13日
    浏览(44)
  • python实现对图片进行均值滤波、中值滤波、高斯滤波处理及其原理和特点

    1.高斯滤波         1)原理:对图像邻域内像素进行平滑时,邻域内不同位置的像素被赋予不同的权值。         2)特点:对图像进行平滑的同时,同时能够更多的保留图像的总体灰度分布特征。         3)代码         4)效果图(左原图)  2.均值滤波         1)

    2024年02月06日
    浏览(44)
  • Opencv-C++笔记 (13) : opencv-图像卷积一(均值、中值、高斯、双边滤波)与 边缘处理

    头文件 quick_opencv.h:声明类与公共函数 主函数调用 src:输入图像 。 dst:输出图像 。 ksize:内核大小 ,一般用 Size(w,h),w 为宽度,h 为深度。 anchor:被平滑的点,表示取 内核中心 ,默认值 Point(-1,-1)。 boderType:推断图像外部像素的某种边界模式。默认值 BORDER_DEFAULT 目的:

    2024年02月16日
    浏览(154)
  • Android 多任务窗口中的界面高斯模糊处理问题

    应用开发过程中安全问题的确需要系统考虑也要开发者自己多考虑,一个小的细节可能会让你的应用变的更安全,最近在用招商银行App的时候无意中发现了 iPhone上多任务窗口 ,看到招商银行的应用有一个特别的地方就是当应用出现在多任务中的时候界面被高斯模糊处理了,

    2024年02月11日
    浏览(39)
  • Unity中Shader的屏幕坐标

    Unity中Shader的屏幕坐标 屏幕归一化坐标 = 当前像素 总像素 屏幕归一化坐标 = frac{当前像素}{总像素} 屏幕归一化坐标 = 总像素 当前像素 ​ _ScreenParams 屏幕的相关参数,单位为像素。 x表示屏幕的宽度 y表示屏幕的高度 z表示1+1/屏幕宽度 w表示1+1/屏幕高度 UNITY_VPOS_TYPE screenPos

    2024年02月09日
    浏览(36)
  • Unity中Shader的屏幕抓取 GrabPass

    Unity中Shader的屏幕抓取 GrabPass 屏幕的抓取需要使用一个Pass GrabPass{} GrabPass{“NAME”} sampler2D _GrabTexture; 测试代码: 效果: 为了优化节省性能:一次抓取就存储下来渲染完,再进行下次抓取 使用:GrabPass{“NAME”} 使用这个抓取后,对应的定义名也要换成 NAME 修改后代码: 注意

    2024年02月07日
    浏览(40)
  • python --opencv图像处理滤波详解(均值滤波、2D 图像卷积、方框滤波、 高斯滤波、中值滤波、双边滤波)

    第一件事情还是先做名词解释,图像平滑到底是个啥? 从字面意思理解貌似图像平滑好像是在说图像滑动。 emmmmmmmmmmmmmmm。。。。 其实半毛钱关系也没有,图像平滑技术通常也被成为图像滤波技术(这个名字看到可能大家会有点感觉)。 每一幅图像都包含某种程度的噪声,

    2024年02月04日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包