• 【Unity3D】高斯模糊特效


    1 高斯模糊原理

    边缘检测特效中使用了卷积运算进行了边缘检测,本文实现的高斯模糊特效同样使用了卷积运算,关于卷积核和卷积运算的概念,读者可以参考边缘检测特效

    ​ 本文完整资源见→Unity3D高斯模糊特效

    ​ 我们将用于模糊处理的卷积核称为模糊算子,它一般满足以下条件:

    • 卷积核中权值上下对称、左右对称;
    • 卷积核中每个权值大于或等于 0,小于 1;
    • 卷积核中所有权值之和为 1。

    ​ 我们将所有权值都为 1 / n(n 为权值个数)的卷积核称为平均模糊算子;将权值随位置变化且符合高斯分布(或正太分布)的卷积核称为高斯模糊算子(或高斯核),它较好地模拟了邻域中每个像素对当前处理像素的影响程度(距离越近,影响越大)。高斯方程如下:

    img

    ​ σ 是标准差,其值越大,高斯分布函数的图像越矮胖,一般取值为 1,x、y 为当前位置到卷积核中心的整数距离。要构建一个高斯核,我们只需要计算高斯核中各个位置对应的高斯值。为了保证模糊处理后的图像不会变暗,我们需要对高斯核中的元素进行归一化,即将所有元素都除以它们的权值和,从而保证归一化后的权值和为 1。因此,高斯函数中 e 前面的系数不会对高斯核产生任何影响,在计算高斯核的过程中可以省去。

    ​ 高斯核的维数越高,模糊程度越大。使用一个 n * n 的卷积核,需要进行 n * n * w * h 次纹理采样(w、h 分别为图像的宽高),为节省性能,我们将二维高斯核拆分为 2 个一维高斯核,采样次数只需要 2 * n * w * h 次。进一步观察到,2 个高斯核中包含了很多重复权重。对于一个大小为 5 的一维高斯核,实际只需记录 3 个权值即可。

    img

    2 代码实现

    ​ GaussianBlur.cs

    using UnityEngine;
    
    [ExecuteInEditMode] // 编辑态可以查看脚本运行效果
    [RequireComponent(typeof(Camera))] // 需要相机组件
    public class GaussianBlur : MonoBehaviour {
        [Range(0, 4)]
        public int iterations = 3; // 高斯模糊迭代次数
        [Range(0.2f, 3.0f)]
        public float blurSpread = 0.6f; // 每次迭代纹理坐标偏移的速度
        [Range(1, 8)]
        public int downSample = 2; // 降采样比率
        private Material material = null; // 材质
    
        private void Start() {
            material = new Material(Shader.Find("MyShader/GaussianBlur"));
            material.hideFlags = HideFlags.DontSave;
        }
    
        void OnRenderImage(RenderTexture src, RenderTexture dest) {
            if (material != null) {
                int rtW = src.width / downSample; // 降采样的纹理宽度
                int rtH = src.height / downSample; // 降采样的纹理高度
                RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
                buffer0.filterMode = FilterMode.Bilinear; // 滤波模式设置为双线性
                Graphics.Blit(src, buffer0);
                for (int i = 0; i < iterations; i++) {
                    material.SetFloat("_BlurSize", 1.0f + i * blurSpread); // 设置模糊尺寸(纹理坐标的偏移量)
                    RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                    Graphics.Blit(buffer0, buffer1, material, 0); // 渲染垂直的Pass
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                    buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                    Graphics.Blit(buffer0, buffer1, material, 1); // 渲染水平的Pass
                    RenderTexture.ReleaseTemporary(buffer0);
                    buffer0 = buffer1;
                }
                Graphics.Blit(buffer0, dest);
                RenderTexture.ReleaseTemporary(buffer0);
            } else {
                Graphics.Blit(src, dest);
            }
        }
    }
    

    ​ GaussianBlur.shader

    Shader "MyShader/GaussianBlur" { // 高斯模糊
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {} // 主纹理
            _BlurSize ("Blur Size", Float) = 1.0 // 模糊尺寸(纹理坐标的偏移量)
        }
    
        SubShader {
            CGINCLUDE
            
            #include "UnityCG.cginc"
            
            sampler2D _MainTex; // 主纹理
            half4 _MainTex_TexelSize; // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
            float _BlurSize; // 模糊尺寸(纹理坐标的偏移量)
              
            struct v2f {
                float4 pos : SV_POSITION; // 模型空间顶点坐标
                half2 uv[5]: TEXCOORD0; // 5个邻域的纹理坐标
            };
              
            v2f vertBlurVertical(appdata_img v) { // 垂直模糊顶点着色器
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
                half2 uv = v.texcoord;
                o.uv[0] = uv;
                o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;      
                return o;
            }
            
            v2f vertBlurHorizontal(appdata_img v) { // 水平模糊顶点着色器
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
                half2 uv = v.texcoord;
                o.uv[0] = uv;
                o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;      
                return o;
            }
    
            fixed4 fragBlur(v2f i) : SV_Target {
                float weight[3] = {0.4026, 0.2442, 0.0545}; // 大小为5的一维高斯核,实际只需记录3个权值
                fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
                for (int j = 1; j < 3; j++) {
                    sum += tex2D(_MainTex, i.uv[j * 2 - 1]).rgb * weight[j]; // 中心右侧或下侧的纹理*权值
                    sum += tex2D(_MainTex, i.uv[j * 2]).rgb * weight[j]; // 中心左侧或上侧的纹理*权值
                }
                return fixed4(sum, 1.0);
            }
                
            ENDCG
            
            ZTest Always Cull Off ZWrite Off
            
            Pass {
                NAME "GAUSSIAN_BLUR_VERTICAL"
                
                CGPROGRAM
                  
                #pragma vertex vertBlurVertical  
                #pragma fragment fragBlur
                  
                ENDCG  
            }
            
            Pass {  
                NAME "GAUSSIAN_BLUR_HORIZONTAL"
                
                CGPROGRAM  
                
                #pragma vertex vertBlurHorizontal  
                #pragma fragment fragBlur
                
                ENDCG
            }
        }
    
        FallBack "Diffuse"
    }
    

    3 运行效果

    1)原图

    img

    2)模糊处理

    ​ 调整模糊迭代次数 iterations 由 0 ~ 4 变化,效果如下:

    img

    ​ 声明:本文转自【Unity3D】高斯模糊特效

  • 相关阅读:
    性能工具之前端分析工Chrome Developer Tools性能标签
    Vue3对话框样式修改不了
    免费的中英文翻译软件-自动批量中英文翻译软件推荐大全
    前端刷题 | 网站
    动态文字闪图怎么做?手把手教你在线做动态闪图
    eval详解
    解决stable diffusion webui1.6 wd1.4 tagger加载失败的问题
    SpringCloud - Spring Cloud Netflix 之 Hystrix Dashboard仪表盘监控(九)
    36.一文讲透JavaScript日期对象Date,时间戳、1970、date方法、date计算
    螺杆支撑座大作用
  • 原文地址:https://www.cnblogs.com/zhyan8/p/17615598.html