0.前言
在学习高斯模糊前我们需要了解几个概念,这样才能帮助我们理解高斯模式是如何实现的。
- 卷积
- 卷积核
1.卷积
在介绍高斯模糊之前,我们需要了解一个概念叫做“卷积”。
它是用来实现高斯模糊的核心算法。
卷积大概是做什么事情的呢?
用大白话来说就是按比例合成自己和周围像素的颜色值。
计算过程大致如下
由于卷积这个概念比较难解释(深度学习中也会用到卷积神经网络),暂且就不解释了,只要了解他是一种运算就行了。
2.卷积核
以上介绍了一种对图像处理的运算过程叫卷积,那卷积核是什么呢?
首先我们看一下如何按照卷积计算得到一个新的像素点
如图所示,我们把某一个像素的相邻像素和自身按照比例进行了混合,用新的值代替原像素。
在处理像素的过程中,中、上下左右的比例如图
这种比例的集合,我们叫做卷积核
百度百科是这样定义的:
卷积核就是图像处理时,给定输入图像,输入图像中一个小区域中像素加权平均后成为输出图像中的每个对应像素,其中权值由一个函数定义,这个函数称为卷积核。
使用卷积核的代码一般都长这样
fixed4 color = tex2D (_MainTex, uv) * 0.5f;
color += tex2D(_MainTex,float2(uv.x - distance,uv.y)) * 0.125f;
color += tex2D(_MainTex,float2(uv.x + distance,uv.y)) * 0.125f;
color += tex2D(_MainTex,float2(uv.x,uv.y + distance)) * 0.125f;
color += tex2D(_MainTex,float2(uv.x,uv.y - distance)) * 0.125f;
3.高斯模糊
当我们知道了卷积和卷积核的概念后,实际上高斯模糊就是应用了一种特殊的卷积核,如图。
拿到了高斯模糊对应的卷积核后,Shader代码写起来就很容易了。
float _Blur;
fixed4 SampleSpriteTexture (float2 uv)
{
// 1 / 16
float offset = _Blur * 0.0625f;
// 左上
fixed4 color = tex2D (_MainTex, float2(uv.x - offset,uv.y - offset)) * 0.0947416f;
// 上
color += tex2D(_MainTex,float2(uv.x,uv.y - offset)) * 0.118318f;
// 右上
color += tex2D(_MainTex,float2(uv.x + offset,uv.y + offset)) * 0.0947416f;
// 左
color += tex2D(_MainTex,float2(uv.x - offset,uv.y)) * 0.118318f;
// 中
color += tex2D(_MainTex,float2(uv.x,uv.y)) * 0.147761f;
// 右
color += tex2D(_MainTex,float2(uv.x + offset,uv.y)) * 0.118318f;
// 左下
color += tex2D (_MainTex, float2(uv.x - offset,uv.y + offset)) * 0.0947416f;
// 下
color += tex2D(_MainTex,float2(uv.x,uv.y + offset)) * 0.118318f;
// 右下
color += tex2D(_MainTex,float2(uv.x + offset,uv.y - offset)) * 0.0947416f;
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
if (_AlphaSplitEnabled)
color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
return color;
}
其实高斯模糊之所以叫高斯模糊,是因为他的卷积核的扩散过程符合正太分布,正态分布又名高斯分布。
所以只要满足正态分布的卷积核,我们就可以叫它高斯核。
4.完整代码
Shader "Starry/Sprite/GaussianBlur"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
// 模糊程度
_Blur("Blur",Range(0,1)) = 0.01
[MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Cull Off
Lighting Off
ZWrite Off
Blend One OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ PIXELSNAP_ON
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
OUT.color = IN.color * _Color;
#ifdef PIXELSNAP_ON
OUT.vertex = UnityPixelSnap (OUT.vertex);
#endif
return OUT;
}
sampler2D _MainTex;
sampler2D _AlphaTex;
float _AlphaSplitEnabled;
float _Blur;
fixed4 SampleSpriteTexture (float2 uv)
{
// 1 / 16
float offset = _Blur * 0.0625f;
// 左上
fixed4 color = tex2D (_MainTex, float2(uv.x - offset,uv.y - offset)) * 0.0947416f;
// 上
color += tex2D(_MainTex,float2(uv.x,uv.y - offset)) * 0.118318f;
// 右上
color += tex2D(_MainTex,float2(uv.x + offset,uv.y + offset)) * 0.0947416f;
// 左
color += tex2D(_MainTex,float2(uv.x - offset,uv.y)) * 0.118318f;
// 中
color += tex2D(_MainTex,float2(uv.x,uv.y)) * 0.147761f;
// 右
color += tex2D(_MainTex,float2(uv.x + offset,uv.y)) * 0.118318f;
// 左下
color += tex2D (_MainTex, float2(uv.x - offset,uv.y + offset)) * 0.0947416f;
// 下
color += tex2D(_MainTex,float2(uv.x,uv.y + offset)) * 0.118318f;
// 右下
color += tex2D(_MainTex,float2(uv.x + offset,uv.y - offset)) * 0.0947416f;
#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
if (_AlphaSplitEnabled)
color.a = tex2D (_AlphaTex, uv).r;
#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED
return color;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = SampleSpriteTexture (IN.texcoord) * IN.color;
c.rgb *= c.a;
return c;
}
ENDCG
}
}
}
效果如下