[置顶] UnityShader : 高斯模糊 Gaussian Blur

通常我们会比较在意图片的清晰度,想要图片越精细,那自然会想到像素要高了,像素的意义就是保存更多的颜色信息,这样图片就能表现更多的细节。相反,要达到模糊效果,自然就是将颜色信息舍弃掉,通过不同的算法,得到的模糊效果也会不一样。

普通的模糊算法

比较普通的一种,就是将每个像素的颜色,都与周边的颜色靠拢,丢失自己独特的颜色,也就是取自己与周围颜色的平均值,从而达到模糊的效果。

开始写Shader, 先定义可调节的参数

//因为shader取值都是百分比,所以我们要定出贴图的尺寸来进行计算
_TextureSize ("_TextureSize",Float) = 256
//取值半径
_BlurRadius ("_BlurRadius",Range(1,15) ) = 1

然后编写出取平均颜色的函数

float4 GetBlurColor( float2 uv )
{

    float space = 1.0/_TextureSize; //算出一个像素的空间
    int count = _BlurRadius * 2 +1; //取值范围
    count *= count;

    //将以自己为中心,周围半径的所有颜色相加,然后除以总数,求得平均值
    float4 colorTmp = float4(0,0,0,0);
    for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
    {
        for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
        {
            float4 color = tex2D(_MainTex,uv + float2(x * space,y * space));
            colorTmp += color;
        }
    }
    return colorTmp/count;
}

其他代码我就不贴了,后面介绍高斯模糊的时候贴出完整shader,有兴趣的话可以将这个函数修改进去。
看到没,右边就是模糊后的效果图,我这里设置的取值半径为5

这就是普通的模糊效果,我一开始也分不清高斯模糊有什么特殊的,直到我做了对比之后。。。
我们先来看看高斯模糊的算法

高斯模糊

如上所说,模糊的手段,就是让像素与周围的像素颜色平滑起来。上面使用简单平均,这样其实不是很合理,因为图像都是连续的,越靠近的点关系越密切,越远离的点关系越疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。这个时候我们就需要用到高斯曲线了。

高斯分布,即正态分布曲线,形状大概如下图:
[置顶] UnityShader : 高斯模糊 Gaussian Blur_第1张图片

它就是一个可以计算出符合上面要求的权重分布的函数,对应的二维形式如下:
这里写图片描述

使用高斯分布曲线作为滤镜算法的模糊算法,称之为高斯模糊

高斯模糊算法实现

流程跟上面的一样,只不过这里我们需要用到高斯模糊的曲线函数来计算权重,来替代之前简单的平均取值。
首先我们需要一个求权重的函数

//计算权重
float GetGaussianDistribution( float x, float y, float rho ) {
float g = 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho );
return g * exp( -(x * x + y * y) / (2 * rho * rho) );
}

接下来我们就要开始写具体的模糊函数了。大致思路就是根据位置获取相应的颜色乘以算出来的权重,得出来的颜色信息总和就是最终的颜色。

float4 GetGaussBlurColor( float2 uv )
{
    //算出一个像素的空间
    float space = 1.0/_TextureSize; 
    //参考正态分布曲线图,可以知道 3σ 距离以外的点,权重已经微不足道了。
    //反推即可知道当模糊半径为r时,取σ为 r/3 是一个比较合适的取值。
    float rho = (float)_BlurRadius * space / 3.0;

    //---权重总和
    float weightTotal = 0;
    for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
    {
        for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
        {
            weightTotal += GetGaussianDistribution(x * space, y * space, rho );
        }
    }
    //--------
    float4 colorTmp = float4(0,0,0,0);
    for( int x = -_BlurRadius ; x <= _BlurRadius ; x++ )
    {
        for( int y = -_BlurRadius ; y <= _BlurRadius ; y++ )
        {
            float weight = GetGaussianDistribution( x * space, y * space, rho )/weightTotal;

            float4 color = tex2D(_MainTex,uv + float2(x * space,y * space));
            color = color * weight;
            colorTmp += color;
        }
    }
    return colorTmp;
}

这其中权重我算了两边,因为我们只取了1/3的半径,所以我们需要归一,就是记录总和,然后让每个权重除以这个总和。这样才能保证所有的权重加起来为1,要不然会出现颜色丢失现象,会导致整体颜色变暗。
(这里没有做优化处理,只是为了方便理解。 实际使用中完全可以将权重设定为常量而不需要每次计算。)

效果图对比

下面是取值半径为5的效果对比图。应该能明显看出区别了。
中间的为普通的模糊,好像近视眼一样,有重影
右边高斯模糊就比较平缓

取值半径为10的模糊效果

当然实际使用中不建议取值半径算这么大。可以进行多次模糊迭代计算,效果更佳。我这里介绍的是二维高斯运算,可以尝试使用一维运算,然后X,Y各模糊一次。
具体可以参考U3D Image effect 包里面的Blur实现方式,

Shader

剩下的代码贴出,把上面的函数拷贝进去就好了,我就不重复贴了。

Shader "Custom/GaussBlur" {
    Properties {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _TextureSize ("_TextureSize",Float) = 256
        _BlurRadius ("_BlurRadius",Range(1,15) ) = 1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

 Pass {
        CGPROGRAM

          #pragma vertex vert
          #pragma fragment frag
        #include "UnityCG.cginc"


        sampler2D _MainTex;
        int _BlurRadius;
        float _TextureSize;

        struct v2f {
            float4 pos : SV_POSITION;
            float2 uv : TEXCOORD0;
        };


        v2f vert( appdata_img v ) 
        {
            v2f o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.uv = v.texcoord.xy;
            return o;
        } 
        /* 将上面的函数拷贝进来 */

        half4 frag(v2f i) : SV_Target 
        {
            //调用普通模糊
            //return GetBlurColor(i.uv);
            //调用高斯模糊 
            //return GetGaussBlurColor(i.uv);
            return tex2D(_MainTex,i.uv);
        }
        ENDCG
        }
    }
    FallBack "Diffuse"
}

你可能感兴趣的:(shader,高斯模糊)