Unity UI界面局部模糊

这篇具有很好参考价值的文章主要介绍了Unity UI界面局部模糊。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近开发碰到的需求:打开一个弹窗,只在弹窗覆盖的区域下方实现局部模糊,其他地方仍然保持清晰,弹窗的位置不固定。
先看下效果,白色方块是一个RawImage,用来表示一个弹窗,点击按钮,进行局部模糊。
unity边缘模糊,Unity,unity,ui
原理是先对RawImage覆盖的区域进行截屏,然后用Shader做高斯模糊。实际开发中,在弹窗出现前,先对弹窗所覆盖区域进行局部截屏,然后把处理好的RenderTexture赋值给弹窗的背景。
截屏使用的代码参考这篇文章:Unity3D 局部截图、全屏截图、带UI截图三种方法

代码实现

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// 对_rawImage所在区域截屏并模糊处理
/// </summary>
public class 局部模糊 : MonoBehaviour
{
    public RawImage _rawImage;

    public void OnButtonClick()
    {
        RectTransform rectTransform = this.transform.GetComponent<RectTransform>();
        BlurRegion(rectTransform);
    }
    
    /// <summary>
    /// uiRoot是界面根节点
    /// </summary>
    private void BlurRegion(RectTransform uiRoot, int iterations = 3, float blurSpread = 2.0f, int downSample = 2)
    {
        //先将_rawImage的透明度改为0,不影响截屏
        Color c = _rawImage.color;
        c.a = 0;
        _rawImage.color = c;
        StartCoroutine(BlurRegionCoroutine(uiRoot, _rawImage, iterations, blurSpread, downSample));
    }
    
    /// <summary>
    /// 注意:根节点这里Canvas的Render mode为Overlay,其他模式,Canvas的宽高和屏幕宽高如果不一致,则需要转换
    /// </summary>
    private IEnumerator BlurRegionCoroutine(RectTransform uiRoot, RawImage rawImage, int iterations, float blurSpread, int downSample)
    {
        yield return new WaitForEndOfFrame();
        
        RectTransform imageRt = rawImage.rectTransform;
        int imageWidth = (int)(imageRt.rect.width);
        int imageHeight = (int)(imageRt.rect.height);
        Texture2D texture2D = new Texture2D(imageWidth, imageHeight, TextureFormat.RGB24, false);

        //计算rawImage作为uiRoot子物体时的局部坐标,因为rawImage可能是孙子节点,所以需要转换
        Vector3 imagePos = uiRoot.InverseTransformPoint(rawImage.transform.position);
        //计算rawImage左下角坐标,屏幕左下角为原点(0, 0)
        Vector2 imagePivot = imageRt.pivot;
        float leftBottomX = Screen.width * 0.5f - imageWidth * imagePivot.x + imagePos.x;
        float leftBottomY = Screen.height * 0.5f - imageHeight * imagePivot.y + imagePos.y;
        
        //从屏幕读取像素, leftBottomX,leftBottomY 是读取的初始位置,width,height是读取像素的宽度和高度
        texture2D.ReadPixels(new Rect(leftBottomX, leftBottomY, imageWidth, imageHeight), 0, 0);
        texture2D.Apply(false, true);
        
        //使用《Shader入门精要》中用到的高斯模糊
        Shader shader = AssetDatabase.LoadAssetAtPath<Shader>("Assets/Resources/Chapter12-GaussianBlur.shader");
        if (shader == null || rawImage == null)
            yield break;
        var material = new Material(shader);
        if (material == null)
            yield break;

        RenderImage(rawImage, iterations, blurSpread, downSample, material, texture2D);
        Color c = rawImage.color;
        c.a = 1;
        rawImage.color = c;
    }

    private void RenderImage(RawImage rawImage, int iterations, float blurSpread, int downSample, Material material, Texture2D texture2D)
    {
        int rtW = texture2D.width / downSample;
        int rtH = texture2D.height / downSample;
        
        // 首先定义了第一个缓存buffer0,并把src中的图像缩放后存储到buffer0中。在迭代过程中,我们又定义了第二个缓存buffer1。
        // 在执行第一个Pass时,输入是buffer0,输出是buffer1,完毕后首先把buffer0释放,再把结果值buffer1存储到buffer0中,
        // 重新分配buffer1,然后再调用第二个Pass,重复上述过程。迭代完成后,buffer0将存储最终的图像
        RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
        buffer0.filterMode = FilterMode.Bilinear;
        Graphics.Blit(texture2D, buffer0);
        
        for (int i = 0; i < iterations; i++)
        {
            material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
            RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
            
            // Render the vertical pass
            Graphics.Blit(buffer0, buffer1, material, 0);
            
            RenderTexture.ReleaseTemporary(buffer0);
            buffer0 = buffer1;
            buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
            
            // Render the horizontal pass
            Graphics.Blit(buffer0, buffer1, material, 1);
            
            RenderTexture.ReleaseTemporary(buffer0);
            buffer0 = buffer1;
        }

        var buffer = RenderTexture.GetTemporary(buffer0.descriptor); //最终效果
        Graphics.Blit(buffer0, buffer);
        RenderTexture.ReleaseTemporary(buffer0);
        rawImage.texture = buffer;
    }
}

高斯模糊Shader来自《Shader入门精要》,也一并粘贴过来

Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_BlurSize ("Blur Size", Float) = 1.0
	}
	SubShader {
		// CGINCLUDE类似于C++中头文件的功能。由于高斯模糊需要定义两个Pass,但它们使用的片元着色器代码
		// 是完全相同的,使用CGINCLUDE可以避免我们编写两个完全一样的frag函数。
		CGINCLUDE
		
		#include "UnityCG.cginc"
		
		sampler2D _MainTex;  
		half4 _MainTex_TexelSize;
		float _BlurSize;
		  
		struct v2f {
			float4 pos : SV_POSITION;
			half2 uv[5]: TEXCOORD0;
		};
		  
		v2f vertBlurVertical(appdata_img v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			
			half2 uv = v.texcoord;
			// 数组的第一个坐标存储了当前的采样纹理,而剩余的四个坐标则是高斯模糊中对邻域采样时使用的纹理坐标。
			// 我们还和属性_BlurSize相乘来控制采样距离。
			o.uv[0] = uv;
			o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
			o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
			o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
					 
			return o;
		}
		
		v2f vertBlurHorizontal(appdata_img v) {
			v2f o;
			o.pos = UnityObjectToClipPos(v.vertex);
			
			half2 uv = v.texcoord;
			
			o.uv[0] = uv;
			o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
			o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
			o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
			o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
					 
			return o;
		}
		
		fixed4 fragBlur(v2f i) : SV_Target {
			float weight[3] = {0.4026, 0.2442, 0.0545};
			
			fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
			
			for (int it = 1; it < 3; it++) {
				sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
				sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
			}
			
			return fixed4(sum, 1.0);
		}
		    
		ENDCG
		
		ZTest Always Cull Off ZWrite Off
		
		Pass {
			//定义名字,可以在其他shader中使用该pass
			NAME "GAUSSIAN_BLUR_VERTICAL"
			
			CGPROGRAM
			  
			#pragma vertex vertBlurVertical  
			#pragma fragment fragBlur
			  
			ENDCG  
		}
		
		Pass {  
			NAME "GAUSSIAN_BLUR_HORIZONTAL"
			
			CGPROGRAM  
			
			#pragma vertex vertBlurHorizontal  
			#pragma fragment fragBlur
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

边缘柔化

目前模糊效果边缘比较硬,为了优化可以在RawImage上层添加一个RectMask2D,并设置Softness
unity边缘模糊,Unity,unity,ui
而RawImage的RectTransform改成锚定RectMask2D的四条边,这样方便通过RectMask2D来修改范围。
最终效果
unity边缘模糊,Unity,unity,ui文章来源地址https://www.toymoban.com/news/detail-517963.html

到了这里,关于Unity UI界面局部模糊的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity编辑器扩展——自动生成UI界面脚本

    一:前言 对于面板赋值或Find绑定UI组件,我们可以使用一种工具化的方式去自动生成代码并绑定对象,增加效率 分为logic和view,view层是UI界面上的组件,每次都会自动生成并覆盖,logic层是逻辑 二:使用 例如一个UI界面,我们只需要做成预制体并在Project下右键预制体,选择

    2024年02月11日
    浏览(31)
  • 基于unity+c#的随机点名系统(简单UI界面+列表+数组)

    目录 一、功能界面显示 二、UI 1、视频的使用 (1)渲染纹理 (2) 视频铺全屏 (3)视频的调用 2、 下拉文本框的使用(旧版) 3、输入文本框的使用(旧版) 4、更新Test文本和下拉文本框的内容 三、保存之前的记录 1、PlayerPrefs (1)保存数据: (2)读取数据: (3)删除

    2024年04月27日
    浏览(23)
  • 【Unity之UI编程】编写一个面板交互界面需要注意的细节

    👨‍💻个人主页 :@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏 :Unity基础实战 当登录成功后,将玩家信息类通过,UI管理器中的显示加载方法中的委托函数,进行,加载面板后的逻辑处理(在里面执行

    2024年02月05日
    浏览(33)
  • Unity3D学习之UI系统——用NGUI制作游戏登陆界面

    会省略一些东西,可以看我的NGUI的博客 设置UI分辨率自适应 设置Root 的层级 和摄像机渲染的层级为UI 主摄像机不渲染UI 一般都是美术给一个示意图,然后按示意图上拼面板 3.1.1 制作图集 制作两个新图集 3.1.2 拖面板 检查DrawCall 3.1.3 面板基类 创建面板基类, 首先设置成单例

    2024年02月19日
    浏览(63)
  • 【Unity】实用功能开发(一)实现在UI中用RawImage实时展示3D模型(背景透明,并通过UI防止3D场景遮挡)并可以通过分层完成:游戏中的人物状态展示界面,小地图,人物实时头像状态等功能

    有时由于项目效果需要,部分功能的实现受到阻碍,这里收集一些已实现的思路和方法,每次会记录大致需求和遇到的问题,如果有更好的想法,欢迎评论区讨论!!! 目录 功能描述: 需求描述: 实现步骤: ①为需要展示的内容区分层级: ②在场景中添加一个摄像机,并

    2024年02月04日
    浏览(35)
  • Unity用鼠标拖拽UI,UI跟随鼠标移动

    先上效果 继承几个拖拽的接口 IBeginDragHandler, IDragHandler,IEndDragHandler 计算下偏移量,转换下坐标系 限制下可拖拽的范围,我设置的是canvas的大小 欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。 如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~ 声明

    2024年02月06日
    浏览(53)
  • Unity UI框架

    最近在各大网站看了一下 Unity3d 的 UI 框架,各种 UI 框架已经有很多的版本了,各有千秋,有的功能虽然写的完善,但用起来太复杂,有的框架功能不完善,搞个课程就上架了,还有什么 MVC 框架,绕来绕去的看的头都大了,这些根本不想用。 于是我自己就写了一个 UI 框架,

    2024年01月22日
    浏览(23)
  • Unity--UI框架

    先说明该UI框架的作用是用来控制UI面板之间的相互跳转的,使用了UI框架后,最大的用处就可以避免页面切换时复杂的操作,使用UI框架可以更好的管理UI页面,控制页面的显示和关闭也分别只由一个函数控制,极大的优化了代码 先看一张UI框架图  结合上方的图,开始逐步制

    2024年02月06日
    浏览(25)
  • unity 拖拽UI

    我们经常会使用拖拽UI的效果,untiy 为拖拽事件也提供了现成的API,我们只要简单的实现几个接口即可 我们用两种方式来实现拖拽代码,一种是使用MonoBehaviour里的方法,一种是实现UI事件接口,但不论是那种方法,拖拽的逻辑都是没有区别的。以下为拖拽核心代码 实现一:使

    2024年02月11日
    浏览(33)
  • Unity UI 优化技巧

    问题:当 UI Canvas 的任何元素发生变化时,都会影响整个 Canvas。 Canvas 是 Unity UI 的重要组成部分。它创建一个网格来表示放置在其顶部的 UI 元素,在 UI 元素更改时重建网格,并调用 GPU 来渲染实际的用户界面。 创建这些网络可能非常昂贵。UI 元素应该写在组件中,以便可以

    2024年04月14日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包