Unity学习shader笔记[十五]高斯模糊UI仿毛玻璃效果
程序员文章站
2022-07-13 21:34:21
...
简单说一下高斯模糊的思路,在片元函数里面每个片元周围一定距离取得像素值,然后这个像素值的颜色和其像素与原片元的像素的距离作为x值,x对应的正态分布的函数的y值作为比重乘以取得的颜色,然后将这些所有的取得的像素值叠加起来求平均数,得到一个片元经过高斯模糊的效果。
文章给出的效果除了仿毛玻璃效果以外还有仿玻璃上面的贴花效果。
这个效果除了一开始计算的时候,不怎么消耗性能
正态分布函数的性质参考: 正态分布
下面是代码:
using UnityEngine;
using UnityEngine.UI;
[ExecuteInEditMode]
public class GaussianBlur : MonoBehaviour
{
[SerializeField]
private float deviation = 1.0f;
private float currentDeviation = 0.0f;
private Material material;
//https://en.wikipedia.org/wiki/Gaussian_blur
float[] GaussianMatrix = new float[49]{
0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f,
0.00002292f, 0.00078634f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f,
0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f,
0.00038771f, 0.01330373f, 0.11098164f, 0.22508352f, 0.11098164f, 0.01330373f, 0.00038771f,
0.00019117f, 0.00655965f, 0.05472157f, 0.11098164f, 0.05472157f, 0.00655965f, 0.00019117f,
0.00002292f, 0.00078633f, 0.00655965f, 0.01330373f, 0.00655965f, 0.00078633f, 0.00002292f,
0.00000067f, 0.00002292f, 0.00019117f, 0.00038771f, 0.00019117f, 0.00002292f, 0.00000067f
};
void Start()
{
#if UNITY_EDITOR
currentDeviation = 0;
CalculateGaussianMatrix(deviation);
currentDeviation = deviation;
#endif
}
void Update()
{
#if UNITY_EDITOR
if (currentDeviation == deviation) return;
CalculateGaussianMatrix(deviation);
currentDeviation = deviation;
#endif
}
void CalculateGaussianMatrix(float d)
{
int x = 0;
int y = 0;
float sum = 0.0f;
//计算横竖方向的高斯分布曲线
for (x = -3; x <= 3; ++x)
{
string str = "";
for (y = -3; y <= 3; ++y)
{
//正态分布的函数公式
//这里方差μ是0 d代表的是σ
//μ代表的是高斯曲线的中值 即曲线最高点的x值
//高斯模糊 像素颜色占比最重的是需要模糊的像素本身 即像素偏移值是0 所以μ是0
//σ是集中度 越大则正太分布函数的曲线越陡 代表越集中于μ
//用y * 7 + x + 24计算的是【7*7】方差矩阵的每一列的7个元素的高斯分布概率
//并且这样算将每一行的高斯分布概率也求出来了
GaussianMatrix[y * 7 + x + 24] = Mathf.Exp(-(x * x + y * y) / (2.0f * d * d)) / (2.0f * Mathf.PI * d * d);
str += y * 7 + x + 24 + " ";
//因为在GaussianMatrix[1]到GaussianMatrix[49]之间σ越大 曲线越换和
//中间元素的y值没那么高了 所以元素的y值总和sum会下降
sum += GaussianMatrix[y * 7 + x + 24];
}
print(" GaussianBlur CalculateGaussianMatrix " + str);
}
print(" sum " + sum);
//normalize
sum = 1.0f / sum;
for (int i = 0; i < GaussianMatrix.Length; i++)
{
GaussianMatrix[i] *= sum;
}
//meshrender适用
//material = GetComponent<MeshRenderer>().sharedMaterial;
//Image使用
material = GetComponent<Image>().material;
material.SetFloatArray("blurWeight", GaussianMatrix);
}
}
Shader "Custom/GrabPassBlur"
{
Properties
{
_BumpAmt("Distortion", range(0, 2)) = 1
_TintAmt("Tint Amount", Range(0,1)) = 0.1
_TintColor("Tint Color", Color) = (1, 1, 1, 1)
_MainTex("Tint Texture (RGB)", 2D) = "white" {}
_BumpMap("Normalmap", 2D) = "bump" {}
//每个像素向周围取值的偏移像素的跨度
//越大则越模糊 越小则越清楚
_BlurAmt("Blur", Range(0, 10)) = 1
}
SubShader
{
//Queue is Transparent so other objects will be rendered first
Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}
LOD 100
GrabPass {}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appData {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
float4 uvgrab : TEXCOORD1;
};
float _BumpAmt;
float _TintAmt;
float _BlurAmt;
float4 _TintColor;
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _GrabTexture;
float4 _GrabTexture_TexelSize;
//https://en.wikipedia.org/wiki/Gaussian_blur
float blurWeight[49];
half4 blur(half4 col, sampler2D tex, float4 uvgrab) {
float2 offset = 1.0 / _ScreenParams.xy;
for (int i = -3; i <= 3; ++i) {
for (int j = -3; j <= 3; ++j) {
//col += tex2Dproj(tex, uvrgab + float4(_GrabTexture_TexelSize.x * i * _BlurAmt, _GrabTexture_TexelSize.y *j * _BlurAmt, 0.0f, 0.0f)) * blurWeight[j * 7 + i + 24];
//这里float4(offset.x * i * _BlurAmt, offset.y * j * _BlurAmt, 0.0f, 0.0f)
//是向周围像素取偏移值
//每个不同位置的偏移取得的颜色会乘以它自身所占的比重
//这是高斯模糊与均值模糊不同的地方
col += tex2D(tex, uvgrab + float4(offset.x * i * _BlurAmt, offset.y * j * _BlurAmt, 0.0f, 0.0f)) * blurWeight[j * 7 + i + 24];
}
}
return col;
}
v2f vert(appData v) {
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = v.texcoord;
//unity提供的函数 抓取顶点下方对应的片元颜色
o.uvgrab = ComputeGrabScreenPos(o.vertex);
return o;
}
half4 frag(v2f i) : COLOR{
//这里主要是进行了仿玻璃上面的贴花的效果模拟
//对模糊后的像素颜色叠加上了纹理贴图和法线贴图的颜色
//注意的是UI上面的shader没有光照的公式计算也能进行法线贴图的贴合
//而是通过法线贴图上面取得的xy值去取纹理贴图或者屏幕底部的像素去表现
half4 mainColor = tex2D(_MainTex, i.texcoord);
half2 distortion = UnpackNormal(tex2D(_BumpMap, i.texcoord)).rg * _BumpAmt;
half4 col = half4(0, 0, 0, 0);
float4 uvgrab = float4(i.uvgrab.x + distortion.x, i.uvgrab.y + distortion.y,
i.uvgrab.z, i.uvgrab.w);
col = blur(col, _GrabTexture, uvgrab);
return lerp(col, col * mainColor, _TintAmt) * _TintColor;
}
ENDCG
}
}
}
上一篇: 仿bilibili导航条毛玻璃效果
下一篇: wpf实现仿苹果水平滑动效果