2DToolkit背景图的高斯模糊效果Shader实现
1. 前言
公司项目最近需要做一个弹出框背景模糊效果,而我们一直用的UI插件都是2D Toolkit,然后如果是直接拿美术的资源来用的话,会造成原底层的背景图跟模糊后的弹出框背景图两个不同图片的资源浪费,所以就打算着能不能用相关算法直接在Unity里面处理图片使用,然后发现PS里面用的是高斯模糊算法,特意试试。
高斯模糊相关的wiki:https://en.wikipedia.org/wiki/Gaussian_blur
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里面想要的效果差不多。