IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【备忘】Unity 单界面UI多个挖洞实现(shader+mask) -> 正文阅读

[游戏开发]【备忘】Unity 单界面UI多个挖洞实现(shader+mask)

引用:1:UGUIshader扣洞_那个妹子留步的博客-CSDN博客

? ? ? ? ? ?
制作新手引导时,遇到需要在界面显示多个挖洞的情况。
?在翻阅一些博客之后,找到一种实现方法。具体思路是:用shader实现多个挖洞,主挖洞的位置上覆盖一个自己实现,继承于Mask组件的用于点击穿透的组件。

多个挖洞的shader:

Shader "Custom/Holeshader"
{
	Properties
	{
		[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
		_Color ("Tint", Color) = (1,1,1,1)
		
		_StencilComp ("Stencil Comparison", Float) = 8
		_Stencil ("Stencil ID", Float) = 0
		_StencilOp ("Stencil Operation", Float) = 0
		_StencilWriteMask ("Stencil Write Mask", Float) = 255
		_StencilReadMask ("Stencil Read Mask", Float) = 255
 
		_ColorMask ("Color Mask", Float) = 15
 
		//add
		// _Center("Center", vector) = (0, 0, 0, 0)//单个扣洞 面板赋值
		_Silder ("_Silder", Range (0,1000)) = 1000 // sliders
		//add
 
 
		[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
	}
 
	SubShader
	{
		Tags
		{ 
			"Queue"="Transparent" 
			"IgnoreProjector"="True" 
			"RenderType"="Transparent" 
			"PreviewType"="Plane"
			"CanUseSpriteAtlas"="True"
		}
		
		Stencil
		{
			Ref [_Stencil]
			Comp [_StencilComp]
			Pass [_StencilOp] 
			ReadMask [_StencilReadMask]
			WriteMask [_StencilWriteMask]
		}
 
		Cull Off
		Lighting Off
		ZWrite Off
		ZTest [unity_GUIZTestMode]
		Blend SrcAlpha OneMinusSrcAlpha
		ColorMask [_ColorMask]
 
		Pass
		{
			Name "Default"
		CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
 
			#include "UnityCG.cginc"
			#include "UnityUI.cginc"
 
			#pragma multi_compile __ UNITY_UI_ALPHACLIP
			
			struct appdata_t
			{
				float4 vertex   : POSITION;
				float4 color    : COLOR;
				float2 texcoord : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};
 
			struct v2f
			{
				float4 vertex   : SV_POSITION;
				fixed4 color    : COLOR;
				float2 texcoord  : TEXCOORD0;
				float4 worldPosition : TEXCOORD1;
				UNITY_VERTEX_OUTPUT_STEREO
			};
			
			fixed4 _Color;
			fixed4 _TextureSampleAdd;
			float4 _ClipRect;
			float _Silder;
            // float2 _Center;//单个
			uniform float2 _PointsArr[10];
            uniform uint _PointCount;
 
			v2f vert(appdata_t IN)
			{
				v2f OUT;
				UNITY_SETUP_INSTANCE_ID(IN);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
				OUT.worldPosition = IN.vertex;
				OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
 
				OUT.texcoord = IN.texcoord;
				
				OUT.color = IN.color * _Color;
				return OUT;
			}
 
			sampler2D _MainTex;
 
			fixed4 frag(v2f IN) : SV_Target
			{
				half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
				
				color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
				
				#ifdef UNITY_UI_ALPHACLIP
				clip (color.a - 0.001);
				#endif
 
				//-------------------add----------------------
				//循环扣洞
				[loop]
				for(uint index;index<_PointCount;index++){
					color.a*=(distance(IN.worldPosition.xy,_PointsArr[index].xy) > _Silder);
					//1像素渐变透明 以免洞边缘尖锐
					float d = distance(IN.worldPosition.xy,_PointsArr[index].xy);
					if( d > _Silder && d < (_Silder+1)){
						color.a *= (d-_Silder);
					}
				}
				
				//单个扣洞
               	// color.a*=(distance(IN.worldPosition.xy,_Center.xy) > _Silder);
				// //1像素渐变透明 以免洞边缘尖锐
				// float d = distance(IN.worldPosition.xy,_Center.xy);
				// if( d > _Silder && d < (_Silder+1)){
				// 	color.a *= (d-_Silder);
				// }
 
               	color.rgb*= color.a;
               	//-------------------add----------------------
 
 
				return color;
			}
		ENDCG
		}
	}
}

继承于Mask的HoleMask:

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


    public class HoleMask : Mask
    {
        float alpahThreshold = 0.3f;
        public override bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
        {
            if (!isActiveAndEnabled)
            {
                return true;
            }
            //当没有指定精灵时返回true,因为不指定Spirte的时候,Unity将其区域填充为默认的白色,全部区域都是可以响应点击的
            Image _image = this.GetComponent<Image>();
            Sprite _sprite = this.GetComponent<Image>().sprite;
            if (_sprite == null || _image == null)
            {
                return true;
            }

            //坐标系转换 
            Vector2 localPositionPivotRelative;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, sp, eventCamera, out localPositionPivotRelative);
            Rect pixelAdjustedRect = this.GetComponent<Image>().GetPixelAdjustedRect();
            // 转换为以屏幕左下角为原点的坐标系
            var localPosition = new Vector2(localPositionPivotRelative.x + rectTransform.pivot.x * rectTransform.rect.width,
                localPositionPivotRelative.y + rectTransform.pivot.y * rectTransform.rect.height);

            var spriteRect = _sprite.textureRect;
            var maskRect = rectTransform.rect;

            var x = 0;
            var y = 0;
            // 转换为纹理空间坐标
            switch (_image.type)
            {

                case Image.Type.Sliced:
                    {
                        var border = _sprite.border;
                        // x 轴裁剪
                        if (localPosition.x < border.x)
                        {
                            x = Mathf.FloorToInt(spriteRect.x + localPosition.x);
                        }
                        else if (localPosition.x > maskRect.width - border.z)
                        {
                            x = Mathf.FloorToInt(spriteRect.x + spriteRect.width - (maskRect.width - localPosition.x));
                        }
                        else
                        {
                            x = Mathf.FloorToInt(spriteRect.x + border.x +
                                                 ((localPosition.x - border.x) /
                                                 (maskRect.width - border.x - border.z)) *
                                                 (spriteRect.width - border.x - border.z));
                        }
                        // y 轴裁剪
                        if (localPosition.y < border.y)
                        {
                            y = Mathf.FloorToInt(spriteRect.y + localPosition.y);
                        }
                        else if (localPosition.y > maskRect.height - border.w)
                        {
                            y = Mathf.FloorToInt(spriteRect.y + spriteRect.height - (maskRect.height - localPosition.y));
                        }
                        else
                        {
                            y = Mathf.FloorToInt(spriteRect.y + border.y +
                                                 ((localPosition.y - border.y) /
                                                 (maskRect.height - border.y - border.w)) *
                                                 (spriteRect.height - border.y - border.w));
                        }
                    }
                    break;
                case Image.Type.Simple:
                default:
                    {
                        // 转换为统一UV空间
                        x = Mathf.FloorToInt(spriteRect.x + spriteRect.width * localPosition.x / maskRect.width);
                        y = Mathf.FloorToInt(spriteRect.y + spriteRect.height * localPosition.y / maskRect.height);
                    }
                    break;
            }

            // 如果texture导入过程报错,则删除组件
            try
            {
                //if (_sprite.texture.GetPixel(x, y).a < alpahThreshold)
                //{
                //    Debug.Log("click work");
                //}
                return _sprite.texture.GetPixel(x, y).a < alpahThreshold;
            }
            catch (UnityException e)
            {
                Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'" + e.Message);
                return !RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);
            }


        }
    }

界面摆放:

mask物体上挂载带shader的材质:

?obj物体上挂载HoleMask组件(Image图形就是个圆形):

?代码调用:


void Main()
{
    int len = 4;
    Vector4[] vec4Points = new Vector4[len];//传给shader的值

    vec4Points[0] = new Vector4(200, -300, 0, 0);
    vec4Points[1] = new Vector4(100, 200, 0, 0);
    vec4Points[2] = new Vector4(-200, 300, 0, 0);
    vec4Points[3] = new Vector4(-100, -200, 0, 0);
    maskImg.material.SetVectorArray("_PointsArr", vec4Points);
    maskImg.material.SetInt("_PointCount", vec4Points.Length);
}

?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:15:32  更:2022-04-18 18:16:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 21:06:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码