後處理 - 均值模糊

yanghui01發表於2024-03-28

原理

就是取自身以及該畫素周圍的8個畫素的顏色值相加,然後除9取個平均值,得到最終顏色值

效果

因為模糊後會出現一些方形的畫素效果,模糊效果不是很平均,所以均值模糊也叫做盒狀模糊。

c#程式碼

using UnityEngine;

public class BoxBlurEff : MonoBehaviour
{
    public Shader m_Shader;
    public Material m_Material;

    [Range(-3, 3)]
    public float m_BlurOffset = 1;

    void Start()
    {
        InitMaterial();
    }

    private void InitMaterial()
    {
        if (false == SystemInfo.supportsImageEffects)
        {
            Debug.LogWarning("This platform does not support image effects or render textures.");
            this.enabled = false;
            return;
        }

        if (null == m_Material)
        {
            if (null != m_Shader && m_Shader.isSupported)
            {
                m_Material = new Material(m_Shader);
                m_Material.hideFlags = HideFlags.DontSave;
            }
        }
        else if (null != m_Shader && m_Material.shader != m_Shader)
        {
            if (m_Shader.isSupported) //優先shader
            {
                m_Material = new Material(m_Shader);
                m_Material.hideFlags = HideFlags.DontSave;
            }
            else
            {
                m_Material = null;
            }
        }

        if (null != m_Material)
            m_Material.SetFloat("_BlurOffset", m_BlurOffset);
    }

    private void ReleaseRT(RenderTexture rt)
    {
        if (rt != null) RenderTexture.ReleaseTemporary(rt);
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (null == m_Material)
        {
            Graphics.Blit(source, destination);
        }
        else
        {
#if UNITY_EDITOR
            m_Material.SetFloat("_BlurOffset", m_BlurOffset);
#endif
            Graphics.Blit(source, destination, m_Material, 0);
        }
    }
    
}

shader

Shader "My/PostEff/BoxBlur"
{
	CGINCLUDE
	#include "UnityCG.cginc"

	sampler2D _MainTex;
	float4 _MainTex_TexelSize; //貼圖大小資訊, 1/width, 1/height, width, height

	float _BlurOffset;

	fixed4 fragBoxBlur(v2f_img i) : SV_Target //片元著色器(逐畫素)
	{
		//均值模糊, 這邊沒有采樣9個, 只取樣了上下左右4個, 速度更快
		float4 colBlur = 0;
		// (1/width, 1/height, -1/width, -1/height)*_BlueOffset
		float4 blurUV = _MainTex_TexelSize.xyxy * float4(1, 1, -1, -1) * _BlurOffset; //控制取樣上下左右1個畫素還是n個畫素
		colBlur += tex2D(_MainTex, i.uv + blurUV.xy);//1,1
		colBlur += tex2D(_MainTex, i.uv + blurUV.xw);//1,-1
		colBlur += tex2D(_MainTex, i.uv + blurUV.zy);//-1,1
		colBlur += tex2D(_MainTex, i.uv + blurUV.zw);//-1,-1

		half4 col = tex2D(_MainTex, i.uv); //獲取畫素顏色
		col.rgb = colBlur.rgb / 4;
		return col;
	}
	ENDCG

	Properties
	{
		_MainTex("Texture", 2D) = "white" {} //主貼圖
		_BlurOffset("BlurOffset", Float) = 1 //模糊偏移
	}
	SubShader
	{
		Cull Off //剔除關閉
		ZWrite Off //寫入深度buff關閉
		ZTest Always //深度測試開始

		Pass //Pass0
		{
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment fragBoxBlur

			ENDCG
		}

		Pass //Pass1
		{
			CGPROGRAM
			#pragma vertex vert_img
			#pragma fragment fragBoxBlur

			ENDCG
		}
	}

}

雙重模糊

單次的均值模糊,其實效果並不是很好,這邊可以透過雙重模糊來最佳化模糊效果

原理:將圖片解析度降低1倍(降取樣),做一次模糊,再降1倍,做一次模糊;

然後再將解析度升1倍(升取樣),做一次模糊,再升1倍,做一次模糊。

using UnityEngine;

public class BoxBlurEff : MonoBehaviour
{
    public Shader m_Shader;
    public Material m_Material;

    [Range(-3, 3)]
    public float m_BlurOffset = 1;

    public bool m_DoubleBlur;

    private int m_SrcRTWidth;
    private int m_SrcRTHeight;

    private RenderTexture m_RTHalfSize; //一半大小
    private RenderTexture m_RTQuarterSize; //1/4大小

    void Start()
    {
        InitMaterial();
    }

    private void InitMaterial()
    {
        if (false == SystemInfo.supportsImageEffects)
        {
            Debug.LogWarning("This platform does not support image effects or render textures.");
            this.enabled = false;
            return;
        }

        if (null == m_Material)
        {
            if (null != m_Shader && m_Shader.isSupported)
            {
                m_Material = new Material(m_Shader);
                m_Material.hideFlags = HideFlags.DontSave;
            }
        }
        else if (null != m_Shader && m_Material.shader != m_Shader)
        {
            if (m_Shader.isSupported) //優先shader
            {
                m_Material = new Material(m_Shader);
                m_Material.hideFlags = HideFlags.DontSave;
            }
            else
            {
                m_Material = null;
            }
        }

        if (null != m_Material)
            m_Material.SetFloat("_BlurOffset", m_BlurOffset);
    }

    private void ReleaseRT(RenderTexture rt)
    {
        if (rt != null) RenderTexture.ReleaseTemporary(rt);
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (null == m_Material)
        {
            Graphics.Blit(source, destination);
        }
        else
        {
#if UNITY_EDITOR
            m_Material.SetFloat("_BlurOffset", m_BlurOffset);
#endif
            if (m_DoubleBlur)
                DoubleBlur(source, destination);
            else
                Graphics.Blit(source, destination, m_Material, 0);
        }
    }

    private void DoubleBlur(RenderTexture source, RenderTexture destination)
    {
        if (m_SrcRTWidth != source.width || m_SrcRTHeight != source.height)
        {
            m_SrcRTWidth = source.width;
            m_SrcRTHeight = source.height;
            m_RTHalfSize = RenderTexture.GetTemporary((int)(m_SrcRTWidth * 0.5f), (int)(m_SrcRTHeight * 0.5f));
            m_RTQuarterSize = RenderTexture.GetTemporary((int)(m_SrcRTWidth * 0.25f), (int)(m_SrcRTHeight * 0.25f));
        }

        //降取樣
        Graphics.Blit(source, m_RTHalfSize, m_Material, 0);
        Graphics.Blit(m_RTHalfSize, m_RTQuarterSize, m_Material, 1);

        //升取樣
        Graphics.Blit(m_RTQuarterSize, m_RTHalfSize, m_Material, 0);
        Graphics.Blit(m_RTHalfSize, destination, m_Material, 1); 
    }

    void OnDestroy()
    {
        if (null != m_RTHalfSize)
        {
            RenderTexture.ReleaseTemporary(m_RTHalfSize);
            RenderTexture.ReleaseTemporary(m_RTQuarterSize);
            m_RTHalfSize = null;
            m_RTQuarterSize = null;
        }
    }

}

效果

參考

Unity自定義後處理——模糊效果_unity圖片模糊效果-CSDN部落格

UnityShader例項13:螢幕特效之均值模糊(Box Blur) - yjbjingcha - 部落格園 (cnblogs.com)

相關文章