Shader "Hidden/GuideMask"
{
Properties
{
_Rect("Rect",vector)=(0,0,0,0)
_BackgroundColor("_BackgroundColor",Color)=(0,0,0,0.3)
_FrontColor("_FrontColor",Color)=(0,0,0,0.2)
_Radian("_Radian",Range(0,1))=0.5
}
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
Tags{"Queue"="Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float4 _Rect;
fixed4 _BackgroundColor;
fixed4 _FrontColor;
float _Radian;
fixed InCircle(float2 center,float radius,float2 uv)
{
return step(radius,distance(center,uv));
}
float Fit(float2 pos)
{
pos=pos*_ScreenParams.xy;
float2 halfSize= _Rect.zw/2;
float halfWidth=halfSize.x;
float halfHeight=halfSize.y;
float _min= min(halfWidth,halfHeight);
float radius = _min*_Radian;
float2 horizontalsize=halfSize-float2(radius,0);
float2 verticalsize=halfSize-float2(0,radius);
fixed2 h=step(_Rect.xy-horizontalsize,pos)*step(pos,_Rect.xy+horizontalsize);
fixed2 v=step(_Rect.xy-verticalsize,pos)*step(pos,_Rect.xy+verticalsize);
float _r=max(h.x*h.y,v.x*v.y);
float2 center=_Rect.xy;
float witdhBigger=step(halfHeight,halfWidth);
float heightBigger=1-witdhBigger;
float _add=_min*(1-_Radian);
float gap= max(halfWidth,halfHeight)-_min;
float2 lu=center+float2(-gap*witdhBigger,gap*heightBigger)+float2(-_add,_add);
float2 ld=center+float2(-gap*witdhBigger,-gap*heightBigger)+float2(-_add,-_add);
float2 ru=center+float2(gap*witdhBigger,gap*heightBigger)+float2(_add,_add);
float2 rd=center+float2(gap*witdhBigger,-gap*heightBigger)+float2(_add,-_add);
float _c= InCircle(lu,radius,pos)*InCircle(ru,radius,pos)*InCircle(ld,radius,pos)*InCircle(rd,radius,pos);
return max(_r,1-_c);
}
fixed4 frag (v2f i) : SV_Target
{
return lerp(_BackgroundColor,_FrontColor,Fit(i.uv));
}
ENDCG
}
}
}
public class GuideMask : Graphic, ICanvasRaycastFilter
{
[SerializeField] private Vector2 _center;
[SerializeField] private Vector2 _size;
[SerializeField] private Vector2 _margin=Vector2.one*10;
[SerializeField] private float _radian = 0.2f;
[SerializeField] private bool _raycastInRect;
[SerializeField] private bool _raycastPass;
public Vector2 center
{
get { return _center; }
}
public Vector2 size
{
get { return _size; }
}
public Rect rect { get { return new Rect() { size = size, center = center, }; } }
public bool raycastInRect { get { return _raycastInRect; } set { _raycastInRect = value; } }
public bool raycastPass { get { return _raycastPass; } set { _raycastPass = value; } }
public Vector2 margin { get { return _margin; } set { _margin = value; } }
public float radian { get { return _radian; } }
public Color background
{
get
{
if (material == null) return Color.white;
return material.GetColor("_BackgroundColor");
}
set
{
if (material == null) return;
material.SetColor("_BackgroundColor", value);
}
}
public override Color color
{
get
{
if (material == null) return Color.white;
return material.GetColor("_FrontColor");
}
set
{
if (material == null) return;
material.SetColor("_FrontColor", value);
}
}
public void SetRect(Rect rect)
{
this._center = rect.center;
this._size = rect.size;
if (material == null) return;
material.SetVector("_Rect", new Vector4(center.x, center.y, size.x, size.y));
}
public void SetRadian(float radian)
{
this._radian = radian;
if (material == null) return;
material.SetFloat("_Radian", radian);
}
public Rect GetFocusRect(RectTransform trans,Vector2 offset)
{
Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(trans);
Vector2 size = bounds.extents * 2;
Canvas canvas = trans.GetComponentInParent<Canvas>();
Vector2 center = Vector2.zero;
switch (canvas.renderMode)
{
case RenderMode.ScreenSpaceOverlay:
center = RectTransformUtility.WorldToScreenPoint(null, trans.position);
break;
case RenderMode.ScreenSpaceCamera:
case RenderMode.WorldSpace:
center = RectTransformUtility.WorldToScreenPoint(canvas.worldCamera, trans.position);
break;
default:
break;
}
return new Rect(center - (Vector2)bounds.extents + offset - margin/2, size+ margin);
}
public Rect GetFocusRect(Transform trans, Vector2 size, Camera camera)
{
if (camera == null)
camera = Camera.main;
Vector2 center = camera.WorldToScreenPoint(trans.position);
return new Rect() { size = size +margin, center = center - margin / 2, };
}
public void Focus(RectTransform trans, Vector2 offset)
{
SetRect(GetFocusRect(trans,offset));
}
public void Focus(Transform trans, Vector2 size, Camera camera)
{
SetRect(GetFocusRect(trans, size, camera));
}
private bool Contains(Rect rect, Vector2 point)
{
return rect.Contains(point);
}
protected override void Awake()
{
SetRect(this.rect);
SetRadian(this._radian);
}
public bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
{
if (!raycastPass) return true;
if (raycastInRect) return !Contains(rect, sp);
return false;
}
}
[CustomEditor(typeof(GuideMask))]
class GuideMaskEditor : Editor
{
GuideMask gm { get { return target as GuideMask; } }
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
gm.material= EditorGUILayout.ObjectField("Material", gm.material, typeof(Material), false) as Material;
gm.color = EditorGUILayout.ColorField("Color", gm.color);
gm.background = EditorGUILayout.ColorField("Background Color", gm.background);
gm.raycastTarget = EditorGUILayout.Toggle("Raycast Target", gm.raycastTarget);
gm.raycastInRect = EditorGUILayout.Toggle("Raycast In Rect", gm.raycastInRect);
gm.raycastPass = EditorGUILayout.Toggle("Raycast Pass", gm.raycastPass);
var center = EditorGUILayout.Vector2Field("Center", gm.center);
var size = EditorGUILayout.Vector2Field("Size", gm.size);
gm.margin = EditorGUILayout.Vector2Field("Margin", gm.margin);
var radian= EditorGUILayout.Slider("Radian", gm.radian, 0, 1);
if (EditorGUI.EndChangeCheck())
{
gm.SetRadian(radian);
gm.SetRect(new Rect() { size = size, center = center, });
serializedObject.ApplyModifiedProperties();
}
}
}