欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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
        }
    }
}

相关标签: UGUI Shader