2DToolkit背景图的高斯模糊效果Shader实现

发表于2016-03-08
评论1 5.1k浏览


1.  
前言

公司项目最近需要做一个弹出框背景模糊效果,而我们一直用的UI插件都是2D Toolkit,然后如果是直接拿美术的资源来用的话,会造成原底层的背景图跟模糊后的弹出框背景图两个不同图片的资源浪费,所以就打算着能不能用相关算法直接在Unity里面处理图片使用,然后发现PS里面用的是高斯模糊算法,特意试试。

高斯模糊相关的wikihttps://en.wikipedia.org/wiki/Gaussian_blur

高斯模糊相关的百度百科:http://baike.baidu.com/link?url=oPTdW3CfAOzPKRcfydNsTdbfEmBRtcQfxDdQZkOWCPdeoUbAZyKmzMvT3LlFj-z0qnpDBjBQbGms5pbmOJXElq

 

2.  分析

模糊效果,简单的理解就是把图像上的某个像素点的色值都跟一定范围的周围像素点色值取一定权重值来替代中心像素点的色值。这样的话,图像原本存在明显的边界就会变得不那么明显,达到我们想要的模糊效果。

当然,最开始的时候为了计算效率问题,我也尝试过简单地采用直线分布取九宫格完全平均得到色值来计算中心像素点,但最后的结果就是,得到的模糊图像马赛克效果很严重,这里暂且不表。下面我们直接采用高斯函数正态分布方式取值。

3.  实现

这里为避免过于啰嗦就不再分析具体的算法的推导过程了,大家可以直接去百科上面了解。

高斯一维表达式:

高斯二维表达式:

其中,σr/3已经确保获得大部分的值域了。

同时,结合2dToolkit的图片显示shader代码,下面直接我修改过的shader代码:

  Shader "Custom/TK2DUIBlurEffect" {
    Properties {
           _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
        _TextureSize ("
贴图大小",Float) = 256
        _BlurRadius ("
模糊半径取",Range(1,15) ) = 1
        [MaterialToggle]_BlurEffectTrigger("
模糊效果开关", Float) = 1
    }
    SubShader {
       Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        ZWrite Off Lighting Off Cull Off Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha
        LOD 110
        
        Pass 
        {
            CGPROGRAM
            #pragma vertex vert_vct
            #pragma fragment frag_mult 
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            int _BlurRadius;
            float _TextureSize;
            float _BlurEffectTrigger;

            struct vin_vct 
            {
                fixed4 vertex : POSITION;
                fixed4 color : COLOR;
                fixed2 texcoord : TEXCOORD0;
            };

            struct v2f_vct
            {
                fixed4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                fixed2 texcoord : TEXCOORD0;
            };
            v2f_vct vert_vct(vin_vct v)
            {
                v2f_vct o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.color = v.color;
                o.texcoord = v.texcoord;
                return o;
            }

            //* 
高斯函数实现 ==== 开始 =====*/

             //
高斯模糊重算法
            fixed Get2DGaussWeight(fixed x, fixed y, fixed sigmaDouble, fixed Pi2SigmaDouble) {
                return (1.0f /Pi2SigmaDouble) * exp( -(x * x + y * y) / (2 * sigmaDouble) );
            }

            //
高斯模糊重算法
            fixed Get1DGaussWeight(fixed x, fixed y, fixed sigmaDouble, fixed Pi2SigmaDouble) {
                return (1.0f / sqrt(Pi2SigmaDouble)) * exp( -(x * x) / (2 * sigmaDouble));
            }

            //
高斯模糊
            fixed4 GaussBlurEffect( float2 uv )
            {
                fixed space = 1.0/_TextureSize;
                //
σr/3 
                fixed sigma = (fixed)_BlurRadius/3;
                //
预处理σ的平方
                fixed sigmaDouble = sigma * sigma;
                //
预处理2PIσ平方的
                fixed Pi2SigmaDouble = 2 * 3.14 * sigma * sigma;

                //
计算这个点的一定范围内的权重总值
                fixed weightSum = 0;
                for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
                {
                    for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
                    {
                        weightSum += Get2DGaussWeight(x * space, y * space, sigmaDouble, Pi2SigmaDouble);
                    }
                }
                //
把此范内的所有区域算一遍,然后相加得到最后的色就是中心点的
                fixed4 colorTmp = fixed4(0,0,0,0);
                for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
                {
                    for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
                    {
                        fixed eachWeight = Get2DGaussWeight( x * space, y * space, sigmaDouble, Pi2SigmaDouble)/weightSum;
                        fixed4 color = tex2D(_MainTex, uv + fixed2(x * space,y * space));
                        //
乘以自身的
                        color = color * eachWeight;
                        colorTmp += color;
                    }
                }
                return colorTmp;
            }

            //* 
高斯函数实现 ==== 结束 =====*/

            half4 frag_mult(v2f_vct i) : SV_Target 
            {
                if(_BlurEffectTrigger == 1)
                {  
                    return GaussBlurEffect(i.texcoord) * i.color;
                }
                else
                {
                    return tex2D(_MainTex, i.texcoord) * i.color;
                }
            }

        ENDCG
        }
    }
    FallBack "Diffuse"
}

 

以上关键代码方面我都已经标注了注释,就不详细解释代码了,主要的效果代码在上面的高斯函数实现体中。

4.  结果对比

原图如下:

高斯一维表达式选取不同半径得到的效果图:

 

高斯二维表达式选取不同半径值的效果图:

 

在上面的图效果可以得出我们完全可以用一维表达式去计算模糊的效果,因为一维比二维表达式的效率快上R(选取的半径值)倍。


PS: 效果图上传之后模糊了很多。。其实真实的效果是挺不错的,跟PS里面想要的效果差不多。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引