# 【均值模糊】

# 盒状模糊

注意在实现同样效果的情况下,尽量减少迭代次数。

均值模糊也称为盒状模糊,原理是采样像素点上下左右的像素累加求平均。

把这个 BoxBlur.cs 脚本挂在需要模糊的摄像机上。

Assets\ScreenAfterEffect\Blur\BoxBlur.cs

using UnityEngine;

[ExecuteInEditMode()]
public class BoxBlur : MonoBehaviour
{
    public Material material;

    [Range(0,10)]
    public int _Iteration = 4;

    [Range(0,15)]
    public float _BlurRadius = 5.0f;

    void Start()
    {
        if(material == null || material.shader == null || material.shader.isSupported == false)
        {
            enabled = false;
            return;
        }
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest) {
        int width = (int)src.width/4; // 除以4是为了提升性能
        int height = (int)src.height/4;
        RenderTexture RT1 = RenderTexture.GetTemporary(width,height);
        RenderTexture RT2 = RenderTexture.GetTemporary(width,height);
        Graphics.Blit(src, RT1,material,0);
        material.SetVector("_BlurOffset", new Vector4(_BlurRadius/width,_BlurRadius/height,0,0));
        for (int i = 0; i < _Iteration; i++)
        {
            Graphics.Blit(RT1,RT2 ,material,0);
            Graphics.Blit(RT2,RT1 ,material,0);
        }
        Graphics.Blit(RT1,dest,material,0);
        RenderTexture.ReleaseTemporary(RT1);
        RenderTexture.ReleaseTemporary(RT2);
    }
}

BoxBlur 使用的 material 使用以下Shader 进行创建。

Assets\ScreenAfterEffect\Blur\BoxBlurShader.shader

Shader "Hidden/BoxBlurShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _BlurOffset("BlurOffset",Float) = 1.0
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert_img
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _BlurOffset;

            fixed4 frag (v2f_img i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                half4 d = _BlurOffset.xyxy * half4(-1,-1,1,1);
                half4 s = 0;
                s += tex2D(_MainTex,i.uv + d.xy);
                s += tex2D(_MainTex,i.uv + d.zy);
                s += tex2D(_MainTex,i.uv + d.xw);
                s += tex2D(_MainTex,i.uv + d.zw);
                s *= 0.25;
                return s;
            }
            ENDCG
        }
    }
}

# 双重模糊

模糊效果更好,这种更加适用于游戏中使用,也适合用于泛光Bloom。

Shader代码与上面的一致。

通过模糊过程中缩小采样,然后放大,减少相邻的格子重影效果。

Assets\ScreenAfterEffect\Blur\DualBlur.cs

using UnityEngine;

[ExecuteInEditMode()]
public class DualBlur : MonoBehaviour
{
    public Material material;

    [Range(0,4)]
    public int _Iteration = 4;

    [Range(0,15)]
    public float _BlurRadius = 5.0f;

    void Start()
    {
        if(material == null || material.shader == null || material.shader.isSupported == false)
        {
            enabled = false;
            return;
        }
    }

    private void OnRenderImage(RenderTexture src, RenderTexture dest) {
        int width = (int)src.width;
        int height = (int)src.height;
        RenderTexture RT1 = RenderTexture.GetTemporary(width,height);
        RenderTexture RT2 = RenderTexture.GetTemporary(width,height);
        Graphics.Blit(src, RT1,material,0);
        material.SetVector("_BlurOffset", new Vector4(_BlurRadius/width,_BlurRadius/height,0,0));
        // 缩小
        for (int i = 0; i < _Iteration; i++)
        {
            width = width/2;
            height = height/2;
            RenderTexture.ReleaseTemporary(RT2);
            RT2 = RenderTexture.GetTemporary(width,height);
            Graphics.Blit(RT1,RT2 ,material,0);

            width = width/2;
            height = height/2;
            RT1 = RenderTexture.GetTemporary(width,height);
            RenderTexture.ReleaseTemporary(RT1);
            Graphics.Blit(RT2,RT1 ,material,0);
        }

        // 放大
        for (int i = 0; i < _Iteration; i++)
        {
            width = width*2;
            height = height*2;
            RenderTexture.ReleaseTemporary(RT2);
            RT2 = RenderTexture.GetTemporary(width,height);
            Graphics.Blit(RT1,RT2 ,material,0);

            width = width*2;
            height = height*2;
            RT1 = RenderTexture.GetTemporary(width,height);
            RenderTexture.ReleaseTemporary(RT1);
            Graphics.Blit(RT2,RT1 ,material,0);
        }

        Graphics.Blit(RT1,dest,material,0);
        RenderTexture.ReleaseTemporary(RT1);
        RenderTexture.ReleaseTemporary(RT2);
    }
}