先来张效果图,左边是有高斯模糊的效果
简单说一下高斯模糊的思路,在片元函数里面每个片元周围一定距离取得像素值,然后这个像素值的颜色和其像素与原片元的像素的距离作为x值,x对应的正态分布的函数的y值作为比重乘以取得的颜色,然后将这些所有的取得的像素值叠加起来求平均数,得到一个片元经过高斯模糊的效果。
文章给出的效果除了仿毛玻璃效果以外还有仿玻璃上面的贴花效果。
这个效果除了重新调参计算的时候会卡一点,参数调节好之后,UI底部的图不管怎么变都不怎么消耗性能
运用的新的知识主要包括grabpass和正态分布 grabpass主要是抓取片元覆盖区域下面已被绘制的像素,就是如果我当前片元绘制区域覆盖了其他片元的绘制区域,则这个被覆盖的片元的绘制颜色会被取到 参考 ShaderLab: GrabPass 正态分布函数的性质参考: 正态分布 Demo在这 下面是代码:
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 } } }