引用: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);
}
?
|