Unity UGUI高斯模糊背景效果 简易解决方案

Unity弹窗背景高斯模糊:轻量级Shader实现与性能优化 高斯模糊是一种经典的图像处理技术,通过特定的卷积核算法对像素进行加权平均,模拟镜头失焦的视觉效果。其核心原理在于分离滤波,将二维卷积分解为水平和垂直两个一维操作,从而大幅降低计算复杂度。在游戏开发与UI设计中,高斯模糊能有效提升视觉层次,引导用户聚焦,增强沉浸感。它广泛应用于弹窗、对话框、菜单等需要临时中断用户操作又不完全切断背景联系的场景。本文以Unity引擎为例,详细解析了通过屏幕抓取结合自定义Shader实现局部高斯模糊的工程方案,并深入探讨了降采样、乒乓缓冲等关键性能优化技巧,帮助开发者在移动端高效 阅读详情


本文欢迎转载,转载请标明出处!

功能描述

在我们玩游戏的时候,经常会看到一些游戏会对UI做类似这样的处理,以《明日方舟》为例:
在这里插入图片描述

可以看到,新开的上层UI界面会以下层UI界面的模糊效果图做背景。这种模糊效果可以起到让玩家的注意力集中在上层UI界面的作用,倘若加入了透明度动画强化模糊过度也可以让UI界面的叠加看上去更加流畅和舒适。

这种便是本文想要实现的一个功能:UI背景高斯模糊。

在这里插入图片描述
这种效果的原理可以分为几个部分:

  1. 如何获取下层UI界面的画面抓取;
  2. 如何使用shader处理模糊效果;
  3. 如何统一管理界面的模糊功能;
  4. 功能拓展和坑点小汇总;

接下来笔者将针对上述几个部分逐一讲解并分享这个具体实现方法。

简易界面框架搭建

正式开始实现效果之前,我们需要准备好一个简单的Demo界面框架。笔者使用的是Unity2018.4.16版本,不过具体的实现基本上与版本没有太大关联,不要太低就行。

首先先搭建好一个简易的实现场景。用四个适当拉伸过并赋予不同颜色材质球的Cube组合出一个“游戏世界”场景,方便我们看具体的模糊效果。同时,我们也要养成一个比较好的习惯,将材质球和以后可能会用到的像sprite,shader和prefab等各自开一个文件夹存放。

简单搭建一个场景
Inspector层级和Asset层级
接着是创建一个UI专用的摄像机。摄像机的参数如下:

在这里插入图片描述
特别需要注意的是摄像机的LayerMask要修改成只拍摄UI层。

然后是创建一个Canvas。将Canvas的RenderMode修改为ScreenSpace-Camera,然后将刚才新建的UI摄像机挂载到这个Canvas的RenderCamera上。

同时,修改CanvasScaler的UI Scale Mode为Scale With Screen Size,调整一个适合的分辨率并将Match调成0.5。

在这里插入图片描述
在Canvas中创建两个按钮,用于做点击按钮弹出界面的功能。

在这里插入图片描述

如何获取下层UI界面的画面抓取

抓取画面的方式,比较实用的方法是RenderTexture(渲染纹理,后面简称RT)渲染摄像机拍摄到的画面,并对这张RT进行处理。Shader中有GrabPass抓取屏幕的方法但是这个方法极其不推荐使用,因为这个方法在移动端会有大概率不能正常运作,同时性能开销也比较大。

不过这边的抓取并不是创建一张RT然后放在UI摄像机中直接输出,而是使用MonoBehaviour的OnRenderImage()对当前摄像机拍摄的画面方法进行模糊处理并输出,也就是俗称的屏幕后处理。

// src为当前摄像机拍摄到的RT  dest为目标输出的RT
void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
   
   
    	// 通过这个函数进行渲染,只不过我们要用的是它的其他重载,可以添加材质球的那个
    	// 第二个参数不一定要是dest,也可以是其他RT
    	// 但是在OnRenderImage作用域内,输出dest的渲染必须是最后一次Blit,否则会有Warning警告
    	Graphics.Blit(src, dest);
    }

我们新建一个类:ScreenBlurEffect 。这个类就是我们进行后处理的类,把它挂在UI摄像机下,并先写入下面这段代码:

using System;
using UnityEngine;
// 这段代码保证了挂载的时候一定要有Camera组件
[RequireComponent(typeof(Camera))]
public class ScreenBlurEffect : MonoBehaviour
{
   
   
    RenderTexture final_blur_rt;
    // 模糊后处理的主要方法 
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
   
   
		int width = src.width;
		int height = src.height;
		// 将当前摄像机画面渲染到目标RT上
		final_blur_rt = RenderTexture.GetTemporary(width, height, 0);
		Graphics.Blit(src, final_blur_rt);
		// 我们只是想获得摄像机的画面,所以完事之后别忘了把画面正常输出出去
		Graphics.Blit(src, dest);
    }
}

这样我们就可以获得当前摄像机拍摄到的画面了(虽然这段代码很简单粗糙)。

如何使用shader处理模糊效果

模糊效果我们一般使用的是高斯模糊。

这块的内容网上搜就可以搜到很多教程及源码,这边就简单介绍介绍:
高斯模糊的处理流程简单来说就是对目标图中每个像素周边的n个像素进行采样后,通过特定的权值与当前像素和周边像素的颜色进行相乘累加,并以这个颜色作为最终颜色输出。由于图像中每个像素最终输出都会受到周边像素的影响,所以像素之间的颜色过度就会变得更加“平滑”,从而达成模糊的效果。

我们新建一个Shader文件:BlurShader,并创建一个新的材质球将shader挂在上面,代码如下:

Shader "Unlit/BlurShader"
{
   
   
    Properties
    {
   
   
        _MainTex ("Texture", 2D) = "white" {
   
   } // 主纹理,我们进行模糊处理的对象就是它
        _BlurSize("BlurSize", Range(0, 127)) = 1.0 // 对周边采样的偏移量
    }
    SubShader
    {
   
   
        CGINCLUDE
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            half4 _MainTex_TexelSize; 
            fixed _BlurSize;

            struct v2f{
   
   
                float4 pos : SV_POSITION;
                half2 uv[5] : TEXCOORD0;
            };

            // 水平uv数据扩展采样
            v2f vert_hor(appdata_img v)
            {
   
   
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                half2 uv = v.texcoord;

                // 下标从0开始,分别取到当前像素,偏移1单位和2单位的uv位置
                o.uv[0] = uv;
                o.uv[1] = uv + half2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                o.uv[2] = uv - half2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;

                o.uv[3] = uv + half2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                o.uv[4] = uv - half2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;

                return o;
            }

            // 水平uv数据扩展采样
            v2f vert_ver(appdata_img v)
            {
   
   
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                half2 uv = v.texcoord;

                // 下标从0开始,分别取到当前像素,偏移1单位和2单位的uv位置
                o.uv[0] = uv;
                o.uv[1] = uv + half2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                o.uv[2] = uv - half2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;

                o.uv[3] = uv + half2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                o.uv[4] = uv - half2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;

                return o;
            }

            // 处理模糊片元
            fixed4 frag(v2f i) : SV_TARGET
            {
   
   
                // 模糊算子,分别决定了当前像素,上下(左右)偏移1个单位和2个单位的计算权重
                // 算子决定了模糊的质量,算子越大越复杂效果越好,当然性能上就要差一些
                half weight[3] = {
   
   0.4026, 0.2442, 0.0545};

                // 当前像素片元颜色(乘以权重)
                fixed3 color = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
                // 根据权重叠加上下(左右)像素颜色
                color += tex2D(_MainTex, i.uv[1]).rgb * weight[1];
                color += tex2D(_MainTex, i.uv[2]).rgb * weight[1];
                color += tex2D(_MainTex, i.uv[3]).rgb * weight[2];
                color += tex2D(_MainTex, i.uv[4]).rgb * weight[2];

                return fixed4(color, 1.0);
            }
        ENDCG

        Cull Off
        ZWrite Off
        Pass // 0:处理水平模糊
        {
   
   
            Name "BLUR_HORIZONTAL"
            CGPROGRAM
            #pragma vertex vert_hor
            #pragma fragment frag
            ENDCG
        }

        Pass // 1:处理垂直模糊
        {
   
   
            Name "BLUR_VERTICAL"
            CGPROGRAM
            #pragma vertex vert_ver
            #pragma fragment frag
            ENDCG
        }
    }

    Fallback Off
}

在这里插入图片描述
修改之前的ScreenBlurEffect脚本,修改后的代码如下:

using System;
using UnityEngine;
// 这段代码保证了挂载的时候一定要有Camera组件
[RequireComponent(typeof(Camera))]
public class ScreenBlurEffect : MonoBehaviour
{
   
   
    // 预先定义shader渲染用的pass
    const int BLUR_HOR_PASS = 0;
    const int BLUR_VER_PASS = 1;
    bool is_support; // 判断当前平台是否支持模糊
    
    RenderTexture final_blur_rt;
    RenderTexture temp_rt;
    [SerializeField]
    public Material blur_mat; // 模糊材质球

    // 外部参数
    [Range(0, 127)]
    float blur_size = 1.0f; // 模糊额外散步大小
    [Range(1, 10)]
    public int blur_iteration = 4; // 模糊采样迭代次数
    public float blur_spread = 1; // 模糊散值
    int cur_iterate_num = 1; // 当前迭代次数
    public int blur_down_sample = 4; // 模糊初始降采样比率
    public bool render_blur_effect = false; // 是否开始渲染模糊效果

    void Awake()
    {
   
   
        is_support = SystemInfo.supportsImageEffects;
    }

    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
   
   
		if(is_support && blur_mat != null && render_blur_effect){
   
   
            // 首先对输出的结果做一次降采样,也就是降低分辨率,减小RT图的大小
            int width = src.width / blur_down_sample;
            int height = src.height / blur_down_sample;
            // 将当前摄像机画面渲染到被降采样的RT上
            final_blur_rt = RenderTexture.GetTemporary(width, height, 0);
            Graphics.Blit(src, final_blur_rt);

            cur_iterate_num = 1; // 初始化迭代
            while(cur_iterate_num <= blur_iteration)
            {
   
   
                blur_mat.SetFloat("_BlurSize", (1.0f + cur_iterate_num * blur_spread) * blur_size);  // 设置模糊扩散uv偏移
                temp_rt = RenderTexture.GetTemporary(width, height, 0);  
                // 使用blit的其他重载,针对对应的材质球和pass进行渲染并输出结果
                Graphics.Blit(final_blur_rt, temp_rt, blur_mat, BLUR_HOR_PASS);
                Graphics.Blit(temp_rt, final_blur_rt, blur_mat, BLUR_VER_PASS);
                RenderTexture.ReleaseTemporary(temp_rt);   // 释放临时RT
                cur_iterate_num ++;
            }
            Graphics.Blit(final_blur_rt, dest);
            RenderTexture.ReleaseTemporary(final_blur_rt);  // final_blur_rt作用已经完成,可以回收了
        }
        else{
   
   
            Graphics.Blit(src, dest);
        }
    }
}

UI摄像机参数设置
修改完代码别忘记把blurMat材质球挂载上去
is_support 的作用是获取当前平台是否支持后处理效果,在Awake或者Start时获取都可以。
BLUR_HOR_PASS 和 BLUR_VER_PASS 这两个int字段代表我们在渲染RT时使用的目标材质球中Shader使用哪个Pass。可以看到,代码中的Graphics.Blit出现了使用blurMat材质球的重载,材质参数后面跟上的这个int值代表了使用哪个Pass(Shader中的Pass从0开始计数)

在这里插入图片描述
(PS:当然如果直接使用0和1赋值也是可以的,只不过我们在写代码的时候,还是尽量避免写一些难以理解的“魔法数字”,可以事先声明就声明,后面假设改了shader的pass也可以通过改一个地方就完成修改,而不会因为单纯写数字而出现修改疏漏。)

一次模糊的效果是有限的,我们可以进行一次模糊之后,对被模糊的图继续执行模糊。同时,扩大模糊的采样范围,也可以加强模糊的效果。当然,每一次模糊都会执行两次Pass,所以我们要掂量性能和效果之间的平衡,并不是重复模糊次数越多越好。我们可以通过自己调试不同的模糊次数和模糊范围来获得一个相对满意的效果。

此外,我们要尽量避免重复创建一次性临时RT。因此,我们在最开始的时候声明了一个临时RT temp_rt,在OnRenderImage中循环利用,而不是等到需要的时候才声明。在重新写入新的渲染数据前,也需要先释放掉temp_rt中的数据再重新渲染进去。
最后,我们获得了经过几次模糊后的模糊RT final_blur_rt,将其输出到dest。
运行游戏,在Inspector把ScreenBlurEffect的render_blur_effect置为true,就可以看到模糊效果了。

模糊前
模糊后
现在我们只是实现了实时模糊界面的效果,要实现模糊UI背景的效果还需要将模糊后的RT输出到某个界面的图片组件上。

我们需要修改原先的模糊逻辑,把模糊后的RT图单独保存,并且不要改变原先的输出画面。同时,假设我们对不同界面的模糊有不同的需求的话,需要预留控制模糊参数的逻辑。最后就是这个模糊效果只需要在模糊的时候才需要打开,其余的时间不让它运行,减少无谓的渲染次数。

首先是用了一个新的类BlurData来控制模糊的具体参数,并且将代码设计为调用EnableBlurRender接口激活脚本渲染模糊。修改后的ScreenBlurEffect类代码如下:

using System;
using UnityEngine;

public class BlurData{
   
   
    public float blur_size;
    public int blur_iteration;
    public int blur_down_sample;
    public float blur_spread;
}

[RequireComponent(typeof(Camera))]
public class ScreenBlurEffect : MonoBehaviour
{
   
   
    // 预先定义shader渲染用的pass
    const int BLUR_HOR_PASS = 0;
    const int BLUR_VER_PASS = 1;
    bool is_support; // 判断当前平台是否支持模糊
    
    RenderTexture final_blur_rt;
    RenderTexture temp_rt;
    [SerializeField]
    public Material blur_mat; // 模糊材质球

    // 外部参数
    [Range
Unity的URP的自定义后处理效果(三) 之 毛玻璃效果 抓手 本文详说如何在URP管线下做毛玻璃效果。 有两种方法,方法一是用URP自带的OpaqueTexture,但是Opaque是不透明的意思, 所以它的缺陷是半透明的物体它是不会渲染到OpaqueTexture里去的。 方法二是用RendererFeature,本文主要讲第二种方法。 实现原理 先在一帧里用RendererFeature用某个camera截一帧的图。 然后用CommandBuffer做多次高斯模糊,达到这种毛玻璃效果。 最后渲染到一个RenderTexture上。 具体.. 阅读详情

相关推荐

Unity弹窗背景虚化效果实战:5分钟搞定高斯模糊Shader(附完整代码)

本文详细介绍了在Unity中实现弹窗背景虚化效果的高斯模糊Shader编写与应用。通过5分钟快速教程,开发者可以掌握UI专用方案的核心代码和实现步骤,包括双Pass渲染策略、动态模糊强度控制以及移动端优化技巧,显著提升游戏和应用的用户体验。

weixin_29003437的博客 279

Unity UGUI特效库:灰度、模糊、溶解等核心效果原理与实现

在Unity游戏开发中,UI视觉效果是提升用户体验的关键环节。通过Shader编程和材质系统,开发者可以实现丰富的像素级操作,如灰度转换、颜色调整等,这些技术能显著增强界面的视觉反馈和沉浸感。其核心价值在于,以较低的性能开销实现动态的视觉引导,例如按钮状态切换、稀有道具高亮等场景。本文聚焦于UGUI特效库的工程实践,深入解析了灰度、模糊、溶解等效果的实现原理,并提供了基于MaterialPropertyBlock的优化方案,帮助开发者在移动端项目中高效集成这些特效,提升UI品质与性能。

weixin_30357231的博客 351

【Unity 实用工具篇】| Unity中 实现背景模糊效果,简单易用

背景模糊 是一个很实用的功能,它可以帮助游戏在视觉上有更好的专注力和表现力。 实现模糊的种类和方式有很多种,大家自行选择适合自己的就可以了。 下面一起来看下本文实现背景模糊的效果和使用方式吧!

努力前行,总会成为自己心中的那道光 4万+

Unity 如何实现苹果动态模糊遮罩

前言 使用过苹果手机的都知道,苹果手机在上banner滑下来的时候,会有一个动态模糊的透明背景,其实就是一个毛玻璃效果,用户可以看到毛玻璃下面的应用或者列表,依然时活动的状态,这就是我说的苹果的动态模糊效果。见下图 1.实现原理 先说原理,要实现一个动态模糊的遮罩,主要有两个技术难点。 1.模糊效果:模糊的效果肯定是使用shader,有一种模糊大家应该都听说过,那就是’'高斯模糊",高斯模糊的算法是一个最长常用的模糊图像算法,因为高斯模糊的效果非常的好。 2.动态遮罩:动态遮罩,什么意思呢?就是说,我们有一

TxNet 2549

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

2019-04-19 新鲜出炉的原创作品 超实用的 Unity 高斯模糊(Gaussian Blur) 在UGUI中[高效][易用][效果棒]的实用方案 原文地址:https://blog.csdn.net/weixin_43452343/article/details/89407650 应该就是你想要

KUKA机器人编程实战:5分钟搞定组输入与组输出的信号配置(附代码示例)

本文详细介绍了KUKA机器人编程中组输入与组输出的信号配置方法,通过5分钟快速掌握核心技巧。文章包含信号声明语法、典型配置流程及高级应用技巧,并附有代码示例,帮助工程师高效实现机器人与外部设备的通信。

efc123456的博客 847

H3C VRF实验

POP-vpn-instance-RT1]route-distinguisher 11:11 # VPN实例-RT1的路由区分器值11:11。配置路由泄露,其实就是在一个VPN示例中添加另一个VPN实例的下一跳,比如在VPN实例RT1中添加去往SRV的默认路由。但是也有了一个新的问题,那就是RT1、RT2和RT3也能互访了,这让VPN实例变得没有意义了。[POP]ip vpn-instance RT1 #配置vpn实例-RT1。绑定了VPN实例,所以要把路由添加到VPN实例POP中。

m0_68698993的博客 4010

【Java完整版 面试必备】Leetcode Top100题目和答案-滑动窗口篇

【Java完整版 面试必备】Leetcode Top100题目和答案-滑动窗口篇

weixin_43298211的博客 2239

python数据分析与可视化

Python数据分析主要包括数据清洗、数据探索和数据可视化三个部分。数据清洗是数据分析的重要环节,主要是对数据进行预处理,包括缺失值处理、异常值处理、数据类型转换等。数据探索则是通过对数据进行初步分析,了解数据的分布和特征,为后续的数据分析提供基础。数据可视化则是将数据以图形化的方式呈现,帮助人们更好地理解和分析数据。

2301_82032652的博客 7690

基于simulink的车道保持辅助系统(LKA)的仿真与实现

本文介绍了如何利用Simulink实现车道保持辅助系统(LKA)的仿真与建模。首先对比了车道偏离预警(LDW)和LKA的功能差异,强调LKA的主动干预特性。接着详细阐述了LKA系统架构,包括环境感知、决策控制等核心组件,并基于自行车模型建立车辆横向动力学方程。 文章重点演示了Simulink建模步骤:搭建车辆动力学模型、设计LKA控制器(采用LQR算法)、实现执行器接口和驾驶员干预检测。通过四个典型场景的仿真测试,验证了系统在直道行驶、弯道跟踪等工况下的控制性能,并给出了关键评价指标。最后展望了MPC控制、

amy_mhd的博客 222

微信支付接口「系统繁忙 SYSTEM_ERROR」/「文件不存在」报错分析与解决方案 接口开发实现SDK

微信支付的“平台证书模式”只有5年的有效期,过期后微信支付将不再签发新的平台证书,转而要求开发者使用“微信支付公钥”来处理敏感信息加密及回调签名验证。然而,由于以下几点原因,许多开发者在调用 获取平台证书。尽管微信支付在技术迭代上有其必要性,但报错设计过于不友好。诸如“系统繁忙”这样的提示很难直接关联到平台证书失效或公钥切换问题。这样的信息表达,开发者不仅看不懂,还需要花费大量时间分析排查,非常影响效率。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 766

am62x A53与R5F通信

AM62x是Ti的一款异构多核芯片。remoteproc驱动,硬编码(写死)了R5F/M4F固件的名字,这样驱动启动时,就会找硬件编码的固件,下面是其对应关系。值得注意的是,linux内核中的M4F remoteproc驱动启动M4F,而linux内核中的R5F remoreteproc驱动不启动R5F,相反,它attach到已经在boot阶段启动的R5F固件上。在/lib/firmware的真实固件名:,而我们生成的固件名如下所示,没有直接对应的,经确认,.out文件就是我们需要的,重命名后即可。

鹰之翔 1351

CMOS图像传感器——Remosaic技术

对remosaic技术进行了介绍

沧海一升的博客 9569

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

下一篇: Unity经典四连无提示报错解决方案
ko~ko~da~yo~
博客等级 码龄6年 27粉丝 4原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值