後處理 - 高斯模糊

yanghui01發表於2024-03-29

原理

取樣5x5範圍的畫素(即25個畫素),然後按中間往外減少的權重值,計算出最終顏色值。

效果

c#程式碼

using UnityEngine;

public class GaussianBlurEff : 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()
    {
        if (false == SystemInfo.supportsImageEffects)
        {
            Debug.LogWarning("This platform does not support image effects or render textures.");
            this.enabled = false;
            return;
        }
        InitMaterial(ref m_Shader, ref m_Material);
        if (null != m_Material)
            m_Material.SetFloat("_BlurOffset", m_BlurOffset);
    }

    private static void InitMaterial(ref Shader s, ref Material mat)
    {
        if (null == mat)
        {
            if (null != s && s.isSupported)
            {
                mat = new Material(s);
                mat.hideFlags = HideFlags.DontSave;
            }
        }
        else if (null != s && mat.shader != s)
        {
            if (s.isSupported) //優先shader
            {
                mat = new Material(s);
                mat.hideFlags = HideFlags.DontSave;
            }
            else
            {
                mat = null;
            }
        }
    }

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

}

shader

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

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

	//高斯模糊橫向
	half4 frag_HorizontalBlur(v2f_img i) : SV_Target
	{
		half2 uv1 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * -2.0; //-2/width * _BlurOffset
		half2 uv2 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * -1.0; //-1/width * _BlurOffset
		half2 uv3 = i.uv;
		half2 uv4 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * 1.0; //1/width * _BlurOffset
		half2 uv5 = i.uv + _MainTex_TexelSize.xy* half2(1, 0)*_BlurOffset * 2.0; //2/width * _BlurOffset

		half4 s = 0;
		//權重從中間外往減少
		s += tex2D(_MainTex, uv1) * 0.05;
		s += tex2D(_MainTex, uv2) * 0.25;
		s += tex2D(_MainTex, uv3) * 0.40; //中間權重
		s += tex2D(_MainTex, uv4) * 0.25;
		s += tex2D(_MainTex, uv5) * 0.05;
		return s;
	}

	//高斯模糊縱向
	half4 frag_VerticalBlur(v2f_img i) : SV_Target
	{
		half2 uv1 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * -2.0; //-2/height * _BlurOffset
		half2 uv2 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * -1.0; //-1/height * _BlurOffset
		half2 uv3 = i.uv;
		half2 uv4 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * 1.0; //1/height * _BlurOffset
		half2 uv5 = i.uv + _MainTex_TexelSize.xy* half2(0, 1)*_BlurOffset * 2.0; //2/height * _BlurOffset

		half4 s = 0;
		//權重從中間外往減少
		s += tex2D(_MainTex, uv1) * 0.05;
		s += tex2D(_MainTex, uv2) * 0.25;
		s += tex2D(_MainTex, uv3) * 0.40; //中間權重
		s += tex2D(_MainTex, uv4) * 0.25;
		s += tex2D(_MainTex, uv5) * 0.05;
		return s;
	}
	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 frag_HorizontalBlur

			ENDCG
		}

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

			ENDCG
		}

	}

}

參考

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

相關文章