Unity高斯模糊(Gaussian Blur)在UGUI中[高效][易用][效果棒]的实用方案

这篇文章是在 下面帖子的基础上做的实际应用
再此先感谢前辈做出的贡献
本文只做最 实用的信息 的描述,具体实现原理还请参考原文

作者:浅墨_毛星云 来源:CSDN 原文:https://blog.csdn.net/poem_qianmo/article/details/51871531


越来越多的游戏使用 高斯模糊UI面板背景
但在网上很少能够找到一个真正可用的版本用于实际项目中
所以在经过几天查资料,实验,并思考之后有了下面的解决方案在此 分享 给大家。


我的思路是:
1.高效
2.易用
3.显示效果佳


高效 和 显示效果 在开头引用文章中的方案已经解决了

代码中有高斯模糊的三个可调参数:
1.DownSampleNum [降采样次数] 向下采样的次数。此值越大,则采样间隔越大,需要处理的像素点越少,运行速度越快。
2.BlurSpreadSize [模糊扩散度]进行高斯模糊时,相邻像素点的间隔。此值越大相邻像素间隔越远,图像越模糊。但过大的值会导致失真。
3.BlurIterations [迭代次数]此值越大,则模糊操作的迭代次数越多,模糊效果越好,但消耗越大。

下面来说实际应用:
高斯模糊作为面板背景,我们想要的是什么?
用比较好的 效果,比较 高效 的获得一张无透明通道高斯模糊屏幕渲染图

我们需要做的是什么?
1.调整上面三个参数,在需求允许的情况下用最高效的参数配置
2.在一次渲染中拿到高斯模糊的RenderTexture(后面简称 RT),赋值到RawImage之后,干掉一切不需要的逻辑和组件
3.在销毁界面后释放RT


易用方面:

目前做到的是只需要拖拽一个prefab到Hierarchy界面就可以当做一张全屏无透明通道的Image作为背景使用。
可以修改RawImage的色值来调整背景图片的色调 可以调亮调暗和这种色调 不过alpha值初始请保持为0

以下是效果图:
包内的资源
Hierarchy内的层级
参数1
参数2
Game画面播放前
Game画面播放后
Shader代码(转载的):

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "UI/UIGaussianBlurLayer"
{
	//-----------------------------------【属性 || Properties】------------------------------------------
	Properties
	{
		//主纹理
		_MainTex("Base (RGB)", 2D) = "white" {}
	}

	//----------------------------------【子着色器 || SubShader】---------------------------------------
	SubShader
	{
		ZWrite Off
		Blend Off

		//---------------------------------------【通道0 || Pass 0】------------------------------------
		//通道0:降采样通道 ||Pass 0: Down Sample Pass
		Pass
		{
			ZTest Off
			Cull Off

			CGPROGRAM

			//指定此通道的顶点着色器为vert_DownSmpl
			#pragma vertex vert_DownSmpl
			//指定此通道的像素着色器为frag_DownSmpl
			#pragma fragment frag_DownSmpl

			ENDCG
		}

		//---------------------------------------【通道1 || Pass 1】------------------------------------
		//通道1:垂直方向模糊处理通道 ||Pass 1: Vertical Pass
		Pass
		{
			ZTest Always
			Cull Off

			CGPROGRAM

			//指定此通道的顶点着色器为vert_BlurVertical
			#pragma vertex vert_BlurVertical
			//指定此通道的像素着色器为frag_Blur
			#pragma fragment frag_Blur

			ENDCG
		}

		//---------------------------------------【通道2 || Pass 2】------------------------------------
		//通道2:水平方向模糊处理通道 ||Pass 2: Horizontal Pass
		Pass
		{
			ZTest Always
			Cull Off

			CGPROGRAM

			//指定此通道的顶点着色器为vert_BlurHorizontal
			#pragma vertex vert_BlurHorizontal
			//指定此通道的像素着色器为frag_Blur
			#pragma fragment frag_Blur

			ENDCG
		}
	}

	//-------------------------CG着色语言声明部分 || Begin CG Include Part----------------------
	CGINCLUDE

	//【1】头文件包含 || include
	#include "UnityCG.cginc"

	//【2】变量声明 || Variable Declaration
	sampler2D _MainTex;
	//UnityCG.cginc中内置的变量,纹理中的单像素尺寸|| it is the size of a texel of the texture
	uniform half4 _MainTex_TexelSize;
	//C#脚本控制的变量 || Parameter
	uniform half _DownSampleValue;

	//【3】顶点输入结构体 || Vertex Input Struct
	struct VertexInput
	{
		//顶点位置坐标
		float4 vertex : POSITION;
		//一级纹理坐标
		half2 texcoord : TEXCOORD0;
	};

	//【4】降采样输出结构体 || Vertex Input Struct
	struct VertexOutput_DownSmpl
	{
		//像素位置坐标
		float4 pos : SV_POSITION;
		//一级纹理坐标(右上)
		half2 uv20 : TEXCOORD0;
		//二级纹理坐标(左下)
		half2 uv21 : TEXCOORD1;
		//三级纹理坐标(右下)
		half2 uv22 : TEXCOORD2;
		//四级纹理坐标(左上)
		half2 uv23 : TEXCOORD3;
	};

	//【5】准备高斯模糊权重矩阵参数7x4的矩阵 ||  Gauss Weight
	static const half4 GaussWeight[7] =
	{
		half4(0.0205,0.0205,0.0205,0),
		half4(0.0855,0.0855,0.0855,0),
		half4(0.232,0.232,0.232,0),
		half4(0.324,0.324,0.324,1),
		half4(0.232,0.232,0.232,0),
		half4(0.0855,0.0855,0.0855,0),
		half4(0.0205,0.0205,0.0205,0)
	};

	//【6】顶点着色函数 || Vertex Shader Function
	VertexOutput_DownSmpl vert_DownSmpl(VertexInput v)
	{
		//【6.1】实例化一个降采样输出结构
		VertexOutput_DownSmpl o;

		//【6.2】填充输出结构
		//将三维空间中的坐标投影到二维窗口
		o.pos = UnityObjectToClipPos(v.vertex);
		//对图像的降采样:取像素上下左右周围的点,分别存于四级纹理坐标中
		o.uv20 = v.texcoord + _MainTex_TexelSize.xy* half2(0.5h, 0.5h);;
		o.uv21 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, -0.5h);
		o.uv22 = v.texcoord + _MainTex_TexelSize.xy * half2(0.5h, -0.5h);
		o.uv23 = v.texcoord + _MainTex_TexelSize.xy * half2(-0.5h, 0.5h);

		//【6.3】返回最终的输出结果
		return o;
	}

	//【7】片段着色函数 || Fragment Shader Function
	fixed4 frag_DownSmpl(VertexOutput_DownSmpl i) : SV_Target
	{
		//【7.1】定义一个临时的颜色值
		fixed4 color = (0,0,0,0);

		//【7.2】四个相邻像素点处的纹理值相加
		color += tex2D(_MainTex, i.uv20);
		color += tex2D(_MainTex, i.uv21);
		color += tex2D(_MainTex, i.uv22);
		color += tex2D(_MainTex, i.uv23);

		//【7.3】返回最终的平均值
		return color / 4;
	}

	//【8】顶点输入结构体 || Vertex Input Struct
	struct VertexOutput_Blur
	{
		//像素坐标
		float4 pos : SV_POSITION;
		//一级纹理(纹理坐标)
		half4 uv : TEXCOORD0;
		//二级纹理(偏移量)
		half2 offset : TEXCOORD1;
	};

	//【9】顶点着色函数 || Vertex Shader Function
	VertexOutput_Blur vert_BlurHorizontal(VertexInput v)
	{
		//【9.1】实例化一个输出结构
		VertexOutput_Blur o;

		//【9.2】填充输出结构
		//将三维空间中的坐标投影到二维窗口
		o.pos = UnityObjectToClipPos(v.vertex);
		//纹理坐标
		o.uv = half4(v.texcoord.xy, 1, 1);
		//计算X方向的偏移量
		o.offset = _MainTex_TexelSize.xy * half2(1.0, 0.0) * _DownSampleValue;

		//【9.3】返回最终的输出结果
		return o;
	}

	//【10】顶点着色函数 || Vertex Shader Function
	VertexOutput_Blur vert_BlurVertical(VertexInput v)
	{
		//【10.1】实例化一个输出结构
		VertexOutput_Blur o;

		//【10.2】填充输出结构
		//将三维空间中的坐标投影到二维窗口
		o.pos = UnityObjectToClipPos(v.vertex);
		//纹理坐标
		o.uv = half4(v.texcoord.xy, 1, 1);
		//计算Y方向的偏移量
		o.offset = _MainTex_TexelSize.xy * half2(0.0, 1.0) * _DownSampleValue;

		//【10.3】返回最终的输出结果
		return o;
	}

	//【11】片段着色函数 || Fragment Shader Function
	half4 frag_Blur(VertexOutput_Blur i) : SV_Target
	{
		//【11.1】获取原始的uv坐标
		half2 uv = i.uv.xy;

		//【11.2】获取偏移量
		half2 OffsetWidth = i.offset;
		//从中心点偏移3个间隔,从最左或最上开始加权累加
		half2 uv_withOffset = uv - OffsetWidth * 3.0;

		//【11.3】循环获取加权后的颜色值
		half4 color = 0;
		for (int j = 0; j< 7; j++)
		{
			//偏移后的像素纹理值
			half4 texCol = tex2D(_MainTex, uv_withOffset);
			//待输出颜色值+=偏移后的像素纹理值 x 高斯权重
			color += texCol * GaussWeight[j];
			//移到下一个像素处,准备下一次循环加权
			uv_withOffset += OffsetWidth;
		}

		//【11.4】返回最终的颜色值
		return color;
	}

	//-------------------结束CG着色语言声明部分  || End CG Programming Part------------------
	ENDCG

	FallBack Off
}

C#代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

// [ExecuteInEditMode]
public class UIGaussianBlurLayer : MonoBehaviour {

    public UnityEngine.UI.RawImage rImg;
    public Shader m_shr;

    [Range(0, 6), Tooltip("[降采样次数]向下采样的次数。此值越大,则采样间隔越大,需要处理的像素点越少,运行速度越快。")]
    public int DownSampleNum=2;
    [Range(0.0f, 20.0f), Tooltip("[模糊扩散度]进行高斯模糊时,相邻像素点的间隔。此值越大相邻像素间隔越远,图像越模糊。但过大的值会导致失真。")]
    public float BlurSpreadSize=3.0f;
    [Range(0, 8), Tooltip("[迭代次数]此值越大,则模糊操作的迭代次数越多,模糊效果越好,但消耗越大。")]
    public int BlurIterations=3;

    private Camera m_camera;
    private RenderTexture m_rt;
    private Material m_mat;
    private string m_shr_name="UI/UIGaussianBlurLayer";
    private Color m_color;

    #region MaterialGetAndSet
    Material material {
        get {
            if (m_mat==null) {
                m_mat=new Material(m_shr);
                m_mat.hideFlags=HideFlags.HideAndDontSave;
            }
            return m_mat;
        }
    }
    #endregion

    void Start () {
        m_camera = GetComponent<Camera>();
        m_shr=Shader.Find(m_shr_name);
        m_color = rImg.color;
        m_color.a = 1f;
        if (!SystemInfo.supportsImageEffects) {
            enabled=false;
            return;
        }
    }

    private void Cleanup() {
        if (m_mat) Object.DestroyImmediate(m_mat);
        if (rImg.texture) RenderTexture.ReleaseTemporary(m_rt);
    }

    private void OnEnable() {
        Cleanup();
    }

    private void OnDestroy() {
        Cleanup();
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest){
        Graphics.Blit(src, dest);

        if (!gameObject.activeInHierarchy && enabled) return;
        if (!m_camera || !m_shr || m_rt != null) return;

        float widthMod=1.0f / (1.0f * (1 << DownSampleNum));
        material.SetFloat("_DownSampleValue", BlurSpreadSize * widthMod);

        int renderWidth = src.width>>DownSampleNum;
        int renderHeight = src.height>>DownSampleNum;
        m_rt = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, RenderTextureFormat.RGB111110Float);
        m_rt.filterMode = FilterMode.Bilinear;

        Graphics.Blit(src, m_rt, material, 0);
        for (int i = 0; i < BlurIterations; i++) {
            //【2.1】Shader参数赋值
            //迭代偏移量参数
            float iterationOffs = (i * 1.0f);
            //Shader的降采样参数赋值
            material.SetFloat("_DownSampleValue", BlurSpreadSize * widthMod + iterationOffs);
            // 【2.2】处理Shader的通道1,垂直方向模糊处理 || Pass1,for vertical blur
            // 定义一个临时渲染的缓存tempBuffer
            RenderTexture tempBuffer = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, RenderTextureFormat.RGB111110Float);
            // 拷贝m_rt中的渲染数据到tempBuffer,并仅绘制指定的pass1的纹理数据
            Graphics.Blit(m_rt, tempBuffer, material, 1);
            //  清空m_rt
            RenderTexture.ReleaseTemporary(m_rt);
            // 将tempBuffer赋给m_rt,此时m_rt里面pass0和pass1的数据已经准备好
            m_rt = tempBuffer;
            // 【2.3】处理Shader的通道2,竖直方向模糊处理 || Pass2,for horizontal blur
            // 获取临时渲染纹理
            tempBuffer = RenderTexture.GetTemporary(renderWidth, renderHeight, 0, RenderTextureFormat.RGB111110Float);
            // 拷贝m_rt中的渲染数据到tempBuffer,并仅绘制指定的pass2的纹理数据
            Graphics.Blit(m_rt, tempBuffer, m_mat, 2);
            //【2.4】得到pass0、pass1和pass2的数据都已经准备好的m_rt
            // 再次清空m_rt
            RenderTexture.ReleaseTemporary(m_rt);
            // 再次将tempBuffer赋给m_rt,此时m_rt里面pass0、pass1和pass2的数据都已经准备好
            m_rt = tempBuffer;
        }
        rImg.texture = m_rt;
        rImg.color = m_color;
        m_camera.enabled = false;
        enabled = false;
    }
}

附上工程插件源码下载地址:
https://download.csdn.net/download/weixin_43452343/11129310

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值