您的位置:首页 > 移动开发 > Unity3D

UnityShader : 高斯模糊 Gaussian Blur

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

普通的模糊算法

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

开始写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



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

我们先来看看高斯模糊的算法

高斯模糊

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

高斯分布,即正态分布曲线,形状大概如下图:



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



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

高斯模糊算法实现

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

首先我们需要一个求权重的函数

//计算权重
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) ) = 5
}
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 高斯模糊