后处理 - 高斯模糊

原理

采样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博客

Unity Shader 屏幕后效果——高斯模糊 - 汐夜 - 博客园 (cnblogs.com)

 

posted @ 2024-03-29 21:58  yanghui01  阅读(3)  评论(0编辑  收藏  举报