User:Baozhenliang/GraphicMask
Appearance
GraphicMask是一个UGUI绘制遮罩的一个组件,一开始是做引导的遮罩时想着怎么做能更高效点,后面做着做着发现引导只是一个遮罩绘制的一个应用,所以就将GraphicMask抽象成一个组件了。
1.UGUIExtend.cs 是UGUI操作的一个静态扩展。
2.Geometry2DUtility.cs 是几何计算工具。
3.Polygon 是多边相关的类。
4.UIGraphicMaskShader.cs 是以shader方式绘制的组件。
5.UIGraphicMaskVertex.cs 是以Vertex方式绘制的组件。
6.方法AddGraphicMask和RemoveGraphicMask是主要的操作函数。
UGUIExtend.cs
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
/// <summary>
/// UGUI扩展
/// </summary>
public static class UGUIExtend
{
#region 固定位置在指定画布区域
/// <summary>
/// 固定位置在指定画布区域
/// </summary>
/// <param name="_source">源位置</param>
/// <param name="_canvas">画布</param>
public static void FixedPositionInCanvas(this RectTransform _source, RectTransform _canvas)
{
Rect mRect = new Rect(Vector2.zero,_canvas.sizeDelta);
_source.FixedPositionInCanvas(_canvas, mRect);
}
/// <summary>
/// 固定位置在指定画布区域内
/// </summary>
/// <param name="_source">源位置</param>
/// <param name="_canvas">画布</param>
/// <param name="_rect">限制区域(画布左下角为坐标原点(0,0))</param>
public static void FixedPositionInCanvas(this RectTransform _source, RectTransform _canvas, Rect _rect)
{
Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(_canvas, _source);
Vector2 delta = Vector2.zero;
Vector3 tempCenter = bounds.center;
tempCenter.x += _canvas.sizeDelta.x * 0.5f;
tempCenter.y += _canvas.sizeDelta.y * 0.5f;
bounds.center = tempCenter;
if (bounds.center.x - bounds.extents.x < _rect.x)//target超出area的左边框
{
delta.x += Mathf.Abs(bounds.center.x - bounds.extents.x - _rect.x);
}
else if (bounds.center.x + bounds.extents.x > _rect.width)//target超出area的右边框
{
delta.x -= Mathf.Abs(bounds.center.x + bounds.extents.x - _rect.width);
}
if (bounds.center.y - bounds.extents.y < _rect.y)//target超出area上边框
{
delta.y += Mathf.Abs(bounds.center.y - bounds.extents.y - _rect.y);
}
else if (bounds.center.y + bounds.extents.y > _rect.height)//target超出area的下边框
{
delta.y -= Mathf.Abs(bounds.center.y + bounds.extents.y - _rect.height);
}
//加上偏移位置算出在屏幕内的坐标
_source.anchoredPosition += delta;
}
#endregion
#region UI坐标转换
/// <summary>
/// 世界物体转换为UI坐标
/// </summary>
/// <param name="_worldGo">世界物体</param>
/// <param name="_worldCamera">世界摄像机</param>
/// <param name="_uiCanvas">UI画布</param>
/// <param name="_uiCamera">UI摄像机</param>
/// <returns>UI坐标</returns>
public static Vector2 WorldToLocalPointInRectangle(this Transform _worldGo, Camera _worldCamera, Canvas _uiCanvas, Camera _uiCamera)
{
Vector2 mUIPoint = RectTransformUtility.WorldToScreenPoint(_worldCamera, _worldGo.position);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)_uiCanvas.transform, mUIPoint, _uiCamera, out mUIPoint);
return mUIPoint;
}
/// <summary>
/// 世界物体转换为UI坐标
/// </summary>
/// <param name="_worldPosition">世界坐标</param>
/// <param name="_worldCamera">世界摄像机</param>
/// <param name="_uiCanvas">UI画布</param>
/// <param name="_uiCamera">UI摄像机</param>
/// <returns>UI坐标</returns>
public static Vector2 WorldToLocalPointInRectangle(this Vector3 _worldPosition, Camera _worldCamera, Canvas _uiCanvas, Camera _uiCamera)
{
Vector2 mUIPoint = RectTransformUtility.WorldToScreenPoint(_worldCamera, _worldPosition);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)_uiCanvas.transform, mUIPoint, _uiCamera, out mUIPoint);
return mUIPoint;
}
#endregion
#region Graphic在VertexHelper的本地四角坐标
/// <summary>
/// Graphic在VertexHelper的本地四角坐标
/// </summary>
/// <param name="_graphic">Graphic</param>
/// <param name="_corners">四角坐标</param>
public static void GraphicLocalCornersForVertexHelper(this Graphic _graphic, out Vector3[] _corners)
{
_corners = new Vector3[4];
_graphic.rectTransform.GetWorldCorners(_corners);
for (int i = 0; i < _corners.Length; i++)
{
Vector2 mUIPoint = RectTransformUtility.WorldToScreenPoint(_graphic.canvas.worldCamera, _corners[i]);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
(RectTransform)_graphic.canvas.transform, mUIPoint, _graphic.canvas.worldCamera, out mUIPoint);
_corners[i] = mUIPoint;
}
}
#endregion
#region Graphic在VertexHelper的本地矩形
/// <summary>
/// Graphic在VertexHelper的本地矩形
/// </summary>
/// <param name="_graphic">Graphic</param>
/// <returns>矩形</returns>
public static Rect GraphicLocalRectForVertexHelper(this Graphic _graphic)
{
Vector3[] fourCorners = null;
return GraphicLocalRectForVertexHelper(_graphic,ref fourCorners);
}
#endregion
#region Graphic在VertexHelper的本地矩形
/// <summary>
/// Graphic在VertexHelper的本地矩形
/// </summary>
/// <param name="_graphic">Graphic</param>
/// <param name="_corners">矩形四角</param>
/// <returns>矩形</returns>
public static Rect GraphicLocalRectForVertexHelper(this Graphic _graphic, ref Vector3[] _corners)
{
GraphicLocalCornersForVertexHelper(_graphic, out _corners);
Rect rect = new Rect();
rect.xMin = rect.yMin = float.MaxValue;
rect.xMax = rect.yMax = float.MinValue;
foreach (Vector3 n in _corners)
{
rect.xMin = Mathf.Min(rect.xMin, n.x);
rect.yMin = Mathf.Min(rect.yMin, n.y);
rect.xMax = Mathf.Max(rect.xMax, n.x);
rect.yMax = Mathf.Max(rect.yMax, n.y);
}
return rect;
}
#endregion
#region 转换矩形为Bounds
/// <summary>
/// 转换矩形为Bounds
/// </summary>
/// <param name="_rect">矩形</param>
/// <returns>矩形</returns>
public static Bounds TransRectToBounds(this Rect _rect)
{
Bounds b = new Bounds();
b.center = _rect.center;
b.max = _rect.max;
b.min = _rect.min;
return b;
}
#endregion
#region RectTransform 设置为全伸展
/// <summary>
/// RectTransform设置为全伸展
/// </summary>
/// <param name="_trans">转换</param>
public static void IdentityStreech(this RectTransform _trans)
{
_trans.anchorMax = Vector2.one;
_trans.anchorMin = Vector2.zero;
_trans.pivot = Vector2.one * 0.5f;
_trans.localRotation = Quaternion.identity;
_trans.localScale = Vector3.one;
_trans.anchoredPosition3D = Vector3.zero;
_trans.anchoredPosition = Vector2.zero;
_trans.sizeDelta = Vector2.zero;
}
#endregion
#region AddListener 添加监听事件
/// <summary>
/// 添加监听事件
/// </summary>
/// <param name="_trigger">触发器</param>
/// <param name="_eventTriggerType">事件类型</param>
/// <param name="_callback">回调</param>
public static void AddListener<T>(this EventTrigger _trigger,
EventTriggerType _eventTriggerType,
UnityAction<T> _callback)
where T: BaseEventData
{
UnityAction<BaseEventData> call = new UnityAction<BaseEventData>((data) => { _callback((T)data); });
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = _eventTriggerType;
entry.callback.AddListener(call);
_trigger.triggers.Add(entry);
}
#endregion
}
Geometry2DUtility.cs
using UnityEngine;
/// <summary>
/// 几何学工具
/// </summary>
public sealed class Geometry2DUtility
{
/// <summary>
/// 求三角形的外接圆
/// </summary>
/// <param name="_p1">顶点1</param>
/// <param name="_p2">顶点2</param>
/// <param name="_p3">顶点3</param>
/// <returns>外接圆圆心</returns>
public static Vector2 TriangleCircumCircle(Vector3 _p1, Vector3 _p2, Vector3 _p3)
{
Vector3 center = Vector3.zero;
#region 三角形的外接圆
/*参考文章
* http://blog.sina.com.cn/s/blog_648868460100h2b8.html
* http://jingyan.baidu.com/article/636f38bb3caa88d6b846109d.html
* https://www.zhihu.com/question/40422123?sort=created
* http://blog.sina.com.cn/s/blog_15ff6002b0102xxxf.html
*/
float a1, b1, c1, d1;
float a2, b2, c2, d2;
float a3, b3, c3, d3;
float x1 = _p1.x, y1 = _p1.y, z1 = _p1.z;
float x2 = _p2.x, y2 = _p2.y, z2 = _p2.z;
float x3 = _p3.x, y3 = _p3.y, z3 = _p3.z;
a1 = (y1 * z2 - y2 * z1 - y1 * z3 + y3 * z1 + y2 * z3 - y3 * z2);
b1 = -(x1 * z2 - x2 * z1 - x1 * z3 + x3 * z1 + x2 * z3 - x3 * z2);
c1 = (x1 * y2 - x2 * y1 - x1 * y3 + x3 * y1 + x2 * y3 - x3 * y2);
d1 = -(x1 * y2 * z3 - x1 * y3 * z2 - x2 * y1 * z3 + x2 * y3 * z1 + x3 * y1 * z2 - x3 * y2 * z1);
a2 = 2 * (x2 - x1);
b2 = 2 * (y2 - y1);
c2 = 2 * (z2 - z1);
d2 = x1 * x1 + y1 * y1 + z1 * z1 - x2 * x2 - y2 * y2 - z2 * z2;
a3 = 2 * (x3 - x1);
b3 = 2 * (y3 - y1);
c3 = 2 * (z3 - z1);
d3 = x1 * x1 + y1 * y1 + z1 * z1 - x3 * x3 - y3 * y3 - z3 * z3;
center.x = -(b1 * c2 * d3 - b1 * c3 * d2 - b2 * c1 * d3 + b2 * c3 * d1 + b3 * c1 * d2 - b3 * c2 * d1)
/ (a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1);
center.y = (a1 * c2 * d3 - a1 * c3 * d2 - a2 * c1 * d3 + a2 * c3 * d1 + a3 * c1 * d2 - a3 * c2 * d1)
/ (a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1);
center.z = -(a1 * b2 * d3 - a1 * b3 * d2 - a2 * b1 * d3 + a2 * b3 * d1 + a3 * b1 * d2 - a3 * b2 * d1)
/ (a1 * b2 * c3 - a1 * b3 * c2 - a2 * b1 * c3 + a2 * b3 * c1 + a3 * b1 * c2 - a3 * b2 * c1);
#endregion
return center;
}
/// <summary>
/// 边是否相交
/// </summary>
/// <param name="_edgeA">边A</param>
/// <param name="_edgeB">边B</param>
/// <returns>是否相交</returns>
public static bool EdgeIntersection(PolygonEdge2D _edgeA, PolygonEdge2D _edgeB)
{
Vector3 p1 = _edgeA.vertexA.position;
Vector3 p2 = _edgeA.vertexB.position;
Vector3 q1 = _edgeB.vertexA.position;
Vector3 q2 = _edgeB.vertexB.position;
Vector3 v1 = Vector3.Cross(p2 - p1, q1 - p1).normalized;
Vector3 v2 = Vector3.Cross(p2 - p1, q2 - p1).normalized;
Vector3 v3 = Vector3.Cross(q2 - q1, p1 - q1).normalized;
Vector3 v4 = Vector3.Cross(q2 - q1, p2 - q1).normalized;
return Vector3.Dot(v1, v2) < 0 && Vector3.Dot(v3, v4) < 0;
}
/// <summary>
/// 边是否相交
/// </summary>
/// <param name="_edgeA">边A</param>
/// <param name="_edgeB">边B</param>
/// <param name="_point">交点</param>
/// <returns>是否相交</returns>
public static bool EdgeIntersection(PolygonEdge2D _edgeA, PolygonEdge2D _edgeB, ref Vector3 _point)
{
bool isCross = EdgeIntersection(_edgeA,_edgeB);
if (isCross)
{
float a1 = _edgeA.vertexA.position.x;
float b1 = _edgeA.vertexA.position.y;
float a2 = _edgeA.vertexB.position.x;
float b2 = _edgeA.vertexB.position.y;
float c1 = _edgeB.vertexA.position.x;
float d1 = _edgeB.vertexA.position.y;
float c2 = _edgeB.vertexB.position.x;
float d2 = _edgeB.vertexB.position.y;
_point = Vector3.zero;
_point.x = ((a2 - a1) * (c2 - c1) * (d2 - b2) + (b2 - b1) * (c2 - c1) * a2 - (d2 - d1) * (a2 - a1) * c2) / ((b2 - b1) * (c2 - c1) - (d2 - d1) * (a2 - a1));
_point.y = (b2 - b1) / (a2 - a1) * (_point.x - a2) + b2;
}
return isCross;
}
/// <summary>
/// 点是否在矩形内
/// </summary>
/// <param name="_point">点</param>
/// <param name="_rect">矩形</param>
/// <returns>true:在矩形内,false:不在矩形内</returns>
public static bool IsPointInRect(Vector2 _point, Rect _rect)
{
return _point.x >= _rect.xMin && _point.x <= _rect.xMax
&& _point.y >= _rect.yMin && _point.y <= _rect.yMax;
}
}
Polygon2DUtility.cs
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 三角剖分工具
/// </summary>
public sealed class Polygon2DUtility
{
/// <summary>
/// 生成三角剖分结果
/// </summary>
/// <param name="_vertexs">离散顶点集</param>
/// <returns>三角剖分结果</returns>
public static PolygonDelaunayResult2D BuilderDelaunay2D(List<Vector3> _vertexs)
{
return BuilderDelaunay2D(_vertexs, null);
}
/// <summary>
/// 生成三角剖分结果
/// </summary>
/// <param name="_vertexs">离散顶点集</param>
/// <param name="_polygonBoundsPointIndexs">多边形边界点索引</param>
/// <returns>三角剖分结果</returns>
public static PolygonDelaunayResult2D BuilderDelaunay2D(List<Vector3> _vertexs, List<int> _polygonBoundsPointIndexs)
{
List<PolygonPoint2D> polygonVertexs = new List<PolygonPoint2D>();
Bounds polygonBounds = new Bounds();
PolygonTriangle2D polygonSupperTriangle = new PolygonTriangle2D(new PolygonPoint2D(0, Vector3.zero), new PolygonPoint2D(1, Vector3.zero), new PolygonPoint2D(2, Vector3.zero));
//已完成的剖分三角形
List<PolygonTriangle2D> polygonDelaunayTriangles = new List<PolygonTriangle2D>();
//多边形边界边
Dictionary<int, PolygonEdge2D> polygonBoundsEdgeMaping = new Dictionary<int, PolygonEdge2D>();
if (_vertexs.Count >= 3)
{
#region 分析点
Vector3 max = Vector3.one * float.MinValue;
Vector3 min = Vector3.one * float.MaxValue;
//构建顶点
for (int i = 0; i < _vertexs.Count; i++)
{
polygonVertexs.Add(new PolygonPoint2D(i, _vertexs[i]));
min.x = Mathf.Min(min.x, _vertexs[i].x);
min.y = Mathf.Min(min.y, _vertexs[i].y);
min.z = Mathf.Min(min.z, _vertexs[i].z);
max.x = Mathf.Max(max.x, _vertexs[i].x);
max.y = Mathf.Max(max.y, _vertexs[i].y);
max.z = Mathf.Max(max.z, _vertexs[i].z);
}
PolygonEdge2D tempPolygonEdge2D = null;
if (_polygonBoundsPointIndexs != null && _polygonBoundsPointIndexs.Count > 0)
{
for (int i = 0; i < _polygonBoundsPointIndexs.Count; i++)
{
if (i < _polygonBoundsPointIndexs.Count - 1)
{
tempPolygonEdge2D = new PolygonEdge2D(polygonVertexs[i], polygonVertexs[i + 1]);
}
else
{
tempPolygonEdge2D = new PolygonEdge2D(polygonVertexs[i], polygonVertexs[0]);
}
polygonBoundsEdgeMaping.Add(tempPolygonEdge2D.key, tempPolygonEdge2D);
}
}
//x轴从左往右排序
polygonVertexs.Sort((x, y) => { return x.position.x >= y.position.x ? 1 : -1; });
//y轴从下往上排序
polygonVertexs.Sort((x, y) => { return (x.position.x == y.position.x && x.position.y >= y.position.y) ? 1 : -1; });
//构建一个包含所有点集的三角形
polygonBounds = new Bounds(Vector3.Lerp(min, max, 0.5f), Vector3.zero);
polygonBounds.SetMinMax(min, max);
float raudis = Vector3.Distance(polygonBounds.min, polygonBounds.max) * 0.6f;
Vector3 tvc = polygonBounds.center + Vector3.back * raudis;
Vector3 tvl = polygonBounds.min + Vector3.back * (raudis - Mathf.Abs(polygonBounds.extents.z));
Vector3 axis = tvc - polygonBounds.center;
Vector3 vc = (tvl - tvc).normalized * raudis * 2;
Vector3 tp0 = vc + tvc;
vc = Quaternion.AngleAxis(120, axis) * vc;
Vector3 tp1 = vc + tvc;
vc = Quaternion.AngleAxis(120, axis) * vc;
Vector3 tp2 = vc + tvc;
tp0.z = tp1.z = tp2.z = 0;
polygonSupperTriangle = new PolygonTriangle2D(new PolygonPoint2D(_vertexs.Count, tp0, true), new PolygonPoint2D(_vertexs.Count + 1, tp1, true), new PolygonPoint2D(_vertexs.Count + 2, tp2, true));
#endregion
#region 三角剖分
//缓存的剖分三角形
List<PolygonTriangle2D> tempDelaunayTriangles = new List<PolygonTriangle2D>();
//等待检测的三角形
List<PolygonTriangle2D> validateTriangles = new List<PolygonTriangle2D>() { polygonSupperTriangle };
//从第一个顶点开始,进行三角剖分检测
int count = polygonVertexs.Count;
for (int i = 0; i < count; i++)
{
List<PolygonTriangle2D> delTris = FindDelaunayTriangle(polygonVertexs[i], ref validateTriangles);
if (delTris.Count > 0)
{
tempDelaunayTriangles.AddRange(delTris);
}
}
tempDelaunayTriangles.AddRange(validateTriangles);
#endregion
#region 删除超级三角形
//需要补边的三角形
List<PolygonTriangle2D> pushEdgeTriangles = new List<PolygonTriangle2D>();
int supperPointCount = 0;
foreach (PolygonTriangle2D tri in tempDelaunayTriangles)
{
//缓存三角形有任意点是超三角形的点
supperPointCount = 0;
foreach (PolygonPoint2D v in tri.vertexs)
{
if (v.isSupperPoint)
{
supperPointCount++;
}
}
if (supperPointCount > 0)
{
pushEdgeTriangles.Add(tri);
}
else
{
polygonDelaunayTriangles.Add(tri);
foreach (PolygonEdge2D edge in tri.edges)
{
polygonBoundsEdgeMaping.Remove(edge.key);
}
}
}
#endregion
#region 对删除的超级三角形与边界比对,补充未绘制的边界三角形
//点索引
int pointIndex = _vertexs.Count;
//交点
Vector3 point = Vector3.zero;
//边是否相交
bool isIntersection = false;
//边交点
Dictionary<int, Dictionary<int, PolygonPoint2D>> edgePoint =new Dictionary<int, Dictionary<int, PolygonPoint2D>>();
//三角形被边界线切割后的多边形点
Dictionary<int, PolygonPoint2D> polygonPoints = new Dictionary<int, PolygonPoint2D>();
//检测三角形
foreach (PolygonTriangle2D tri in pushEdgeTriangles)
{
polygonPoints.Clear();
foreach (PolygonEdge2D triEdge in tri.edges)
{
foreach (PolygonEdge2D edge in polygonBoundsEdgeMaping.Values)
{
#region 求交点
isIntersection = false;
point = Vector3.zero;
if (edgePoint.ContainsKey(triEdge.key) && edgePoint[triEdge.key].ContainsKey(edge.key))
{
isIntersection = true;
}
else if (Geometry2DUtility.EdgeIntersection(triEdge, edge, ref point))
{
isIntersection = true;
if (!edgePoint.ContainsKey(triEdge.key))
{
edgePoint.Add(triEdge.key, new Dictionary<int, PolygonPoint2D>());
}
if (!edgePoint[triEdge.key].ContainsKey(edge.key))
{
edgePoint[triEdge.key].Add(edge.key, new PolygonPoint2D(pointIndex, point));
}
polygonVertexs.Add(edgePoint[triEdge.key][edge.key]);
pointIndex++;
}
if (isIntersection)
{
//加入交点
if (!polygonPoints.ContainsKey(edgePoint[triEdge.key][edge.key].index))
{
polygonPoints.Add(edgePoint[triEdge.key][edge.key].index, edgePoint[triEdge.key][edge.key]);
}
}
#endregion
}
}
if (polygonPoints.Count > 0)
{
//如果有任意交点,保留三角形非超级点
foreach (PolygonPoint2D p in tri.vertexs)
{
if (!p.isSupperPoint)
{
if (!polygonPoints.ContainsKey(p.index))
{
polygonPoints.Add(p.index, p);
}
}
}
if (polygonPoints.Count >= 3)
{
List<PolygonPoint2D> pps = new List<PolygonPoint2D>(polygonPoints.Values);
if (pps.Count == 3)
{
polygonDelaunayTriangles.Add(
new PolygonTriangle2D(pps[0], pps[1], pps[2]));
}
else if (pps.Count == 4)
{
//如果有两个交点,则pps中前两个是交点,后两个是原三角形的两个顶点
//交点与顶点各取一点组成两条边,然后看两条边是否相交,相交则是对角线,不相交则不是
//找到对角线后,与非对角线点的另外一个交点和顶点组成新的两个三角形
PolygonEdge2D edge1 = new PolygonEdge2D(pps[0],pps[2]);
PolygonEdge2D edge2 = new PolygonEdge2D(pps[1], pps[3]);
if (Geometry2DUtility.EdgeIntersection(edge1, edge2))
{
//0,2,1 和0,2,3 组成两个新的三角形
polygonDelaunayTriangles.Add(
new PolygonTriangle2D(pps[0], pps[2], pps[1]));
polygonDelaunayTriangles.Add(
new PolygonTriangle2D(pps[0], pps[2], pps[3]));
}
else
{
//0,3,1 和 0,3,2 组成两个新的三角形
polygonDelaunayTriangles.Add(
new PolygonTriangle2D(pps[0], pps[3], pps[1]));
polygonDelaunayTriangles.Add(
new PolygonTriangle2D(pps[0], pps[3], pps[2]));
}
}
}
}
}
#endregion
}
else
{
throw new UnityException("There are not enough vertexs to constitute polygon,the vertexs are least of three.");
}
return new PolygonDelaunayResult2D(polygonVertexs,polygonBounds,polygonSupperTriangle,polygonDelaunayTriangles);
}
/// <summary>
/// 边切割三角形
/// </summary>
/// <param name="_triangle">三角形</param>
/// <param name="_edge">边</param>
/// <returns></returns>
static List<PolygonTriangle2D> EdgeCutTriangle(PolygonTriangle2D _triangle,PolygonEdge2D _edge)
{
List<PolygonTriangle2D> triangles = new List<PolygonTriangle2D>();
return triangles;
}
/// <summary>
/// 找到三角剖分三角形
/// </summary>
/// <param name="_point">点</param>
/// <param name="_validateTriangles">待检测三形</param>
static List<PolygonTriangle2D> FindDelaunayTriangle(PolygonPoint2D _point,ref List<PolygonTriangle2D> _validateTriangles)
{
List<PolygonTriangle2D> result = new List<PolygonTriangle2D>();
List<PolygonTriangle2D> cache = new List<PolygonTriangle2D>();
Dictionary<int, PolygonEdge2D> edges = new Dictionary<int, PolygonEdge2D>();
//两个或两个以上三角形用到的边为共同边,要用等组合边中去掉
Dictionary<int, int> edgeUseNum = new Dictionary<int, int>();
for (int i = 0; i < _validateTriangles.Count; i++)
{
if (!_validateTriangles[i].IsInCircumCircle(_point.position))
{
if (_validateTriangles[i].circumCircleCenter.x + _validateTriangles[i].circumCircleRadius <= _point.position.x)
{
result.Add(_validateTriangles[i]);
}
else
{
cache.Add(_validateTriangles[i]);
}
}
else
{
foreach (PolygonEdge2D d in _validateTriangles[i].edges)
{
if (!edges.ContainsKey(d.key))
{
edges.Add(d.key, d);
}
if (!edgeUseNum.ContainsKey(d.key))
{
edgeUseNum.Add(d.key, 0);
}
edgeUseNum[d.key]++;
}
}
}
//未确定的边重建三角形
foreach (PolygonEdge2D d in edges.Values)
{
//只重建非共用的边,共用边是两个相邻三角形的对角线,不需要再重建了。
if (edgeUseNum[d.key] <= 1)
{
cache.Add(new PolygonTriangle2D(d.vertexA, d.vertexB, _point));
}
}
_validateTriangles = cache;
return result;
}
}
PolygonDelaunayResult2D.cs
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 多边形三角剖分结果
/// </summary>
public class PolygonDelaunayResult2D
{
/// <summary>
/// 网格顶点
/// </summary>
public List<PolygonPoint2D> vertexs { get; private set; }
/// <summary>
/// 包围盒
/// </summary>
public Bounds bounds { get; private set; }
/// <summary>
/// 超级三角形(包含所有点集的三角形)
/// </summary>
public PolygonTriangle2D supperTriangle { get; private set; }
/// <summary>
/// 剖分三角形
/// </summary>
public List<PolygonTriangle2D> delaunayTriangle { get; private set; }
/// <summary>
/// 添加顶点
/// </summary>
/// <param name="_vertexs">顶点</param>
/// <param name="_bounds">包围盒</param>
/// <param name="_supperTriangle">超级三角形</param>
/// <param name="_delaunayTriangle">剖分三角形</param>
public PolygonDelaunayResult2D(List<PolygonPoint2D> _vertexs,Bounds _bounds, PolygonTriangle2D _supperTriangle, List<PolygonTriangle2D> _delaunayTriangle)
{
vertexs = _vertexs;
bounds = _bounds;
supperTriangle = _supperTriangle;
delaunayTriangle = _delaunayTriangle;
}
}
PolygonEdge2D.cs
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 多边形边
/// </summary>
public class PolygonEdge2D
{
/// <summary>
/// 多边形边
/// </summary>
/// <param name="_vertexA">顶点A</param>
/// <param name="_vertexB">顶点B</param>
public PolygonEdge2D(PolygonPoint2D _vertexA, PolygonPoint2D _vertexB)
{
vertexA = _vertexA;
vertexB = _vertexB;
List<int> sort = new List<int>() { _vertexA.index, _vertexB.index };
sort.Sort();
StringBuilder sb = new StringBuilder();
foreach (int k in sort)
{
sb.Append(k);
}
key = sb.ToString().UniqueHashCode();
}
/// <summary>
/// 边Key值
/// </summary>
public int key { get; private set; }
/// <summary>
/// 顶点A
/// </summary>
public PolygonPoint2D vertexA { get; private set; }
/// <summary>
/// 顶点B
/// </summary>
public PolygonPoint2D vertexB { get; private set; }
}
PolygonPoint2D.cs
using UnityEngine;
/// <summary>
/// 多边形点
/// </summary>
public class PolygonPoint2D
{
/// <summary>
/// 多边形点
/// </summary>
/// <param name="_index">点索引</param>
/// <param name="_position">点位置</param>
public PolygonPoint2D(int _index, Vector3 _position)
: this(_index, _position, false)
{
}
/// <summary>
/// 多边形点
/// </summary>
/// <param name="_index">点索引</param>
/// <param name="_position">点位置</param>
/// <param name="_isSupperPoint">是否是超级点</param>
public PolygonPoint2D(int _index, Vector3 _position,bool _isSupperPoint)
{
index = _index;
position = new Vector3(_position.x, _position.y, 0);
isSupperPoint = _isSupperPoint;
}
/// <summary>
/// 索引
/// </summary>
public int index { get; private set; }
/// <summary>
/// 位置
/// </summary>
public Vector3 position { get; private set; }
/// <summary>
/// 是否是超级点
/// </summary>
public bool isSupperPoint { get; private set; }
}
PolygonTriangle2D.cs
using UnityEngine;
/// <summary>
/// 多边形三角形
/// </summary>
public class PolygonTriangle2D
{
/// <summary>
/// 多边形三角形
/// </summary>
/// <param name="_vertexA">顶点A</param>
/// <param name="_vertexB">顶点B</param>
/// <param name="_vertexC">顶点C</param>
public PolygonTriangle2D(PolygonPoint2D _vertexA, PolygonPoint2D _vertexB, PolygonPoint2D _vertexC)
{
//Vector3 ab = (_vertexB.position - _vertexA.position).normalized;
//Vector3 ac = (_vertexC.position - _vertexA.position).normalized;
//Vector3 vc = Vector3.Cross(ab, ac);
//if (vc.z > 0)
//{
// //逆时针三角形,B,C点交换
// PolygonPoint2D p = _vertexC;
// _vertexC = _vertexB;
// _vertexB = p;
//}
vertexs = new PolygonPoint2D[3] { _vertexA, _vertexB, _vertexC };
edges = new PolygonEdge2D[3] { new PolygonEdge2D(_vertexA, _vertexB), new PolygonEdge2D(_vertexB, _vertexC), new PolygonEdge2D(_vertexC, _vertexA) };
circumCircleCenter = Geometry2DUtility.TriangleCircumCircle(_vertexA.position, _vertexB.position, _vertexC.position);
circumCircleRadius = Vector3.Distance(circumCircleCenter, _vertexA.position);
key = (_vertexA.index.ToString() + _vertexB.index.ToString() + _vertexC.ToString()).UniqueHashCode();
}
/// <summary>
/// 点是否在三角形外接圆内
/// </summary>
/// <param name="_point">点</param>
/// <returns>True:是,False:否</returns>
public bool IsInCircumCircle(Vector3 _point)
{
return Vector3.Distance(circumCircleCenter, _point) < circumCircleRadius;
}
/// <summary>
/// 点是否在三角形内
/// </summary>
/// <param name="_point">点</param>
/// <returns>True:是,False:否</returns>
public bool IsInTriangle(Vector3 _point)
{
Vector3 a = vertexs[0].position;
Vector3 b = vertexs[1].position;
Vector3 c = vertexs[2].position;
Vector3 p = _point;
return SameSide(a, b, c, p) &&
SameSide(b, c, a, p) &&
SameSide(c, a, b, p);
}
// Determine whether two vectors v1 and v2 point to the same direction
// v1 = Cross(AB, AC)
// v2 = Cross(AB, AP)
bool SameSide(Vector3 _a, Vector3 _b, Vector3 _c, Vector3 _p)
{
Vector3 ab = _b - _a;
Vector3 ac = _c - _a;
Vector3 ap = _p - _a;
Vector3 v1 = Vector3.Cross(ab, ac);
Vector3 v2 = Vector3.Cross(ab, ap);
// v1 and v2 should point to the same direction
return Vector3.Dot(v1, v2) >= 0;
}
/// <summary>
/// Key
/// </summary>
public int key { get; private set; }
/// <summary>
/// 外接圆半径
/// </summary>
public float circumCircleRadius { get; private set; }
/// <summary>
/// 外接圆圆心
/// </summary>
public Vector3 circumCircleCenter { get; private set; }
/// <summary>
/// 顶点【A,B,C】【逆时针】
/// </summary>
public PolygonPoint2D[] vertexs { get; private set; }
/// <summary>
/// 边【逆时针】
/// </summary>
public PolygonEdge2D[] edges { get; private set; }
}
AbsUIGraphicMask.cs
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 绘制遮罩
/// </summary>
public abstract class AbsUIGraphicMask : MaskableGraphic, ICanvasRaycastFilter
{
/// <summary>
/// 遮罩事件触发分类
/// </summary>
[AliasTooltip("遮罩事件触发分类")]
public enTriggerEventClassiy triggerEventClassify = enTriggerEventClassiy.MaskEnable;
/// <summary>
/// 遮罩绘制分类
/// </summary>
[AliasTooltip("遮罩绘制分类")]
public enGraphicMaskClassify graphicClassify = enGraphicMaskClassify.ExceptMask;
/// <summary>
/// 绘制精灵填充分类
/// </summary>
[AliasTooltip("绘制精灵填充分类")]
public enGraphicSpriteFillClassify graphicSpriteFillClassify = enGraphicSpriteFillClassify.SceneRatioFill;
/// <summary>
/// 精灵图
/// </summary>
[AliasTooltip("精灵图")]
public Sprite sprite;
#region 这个属性必须重写返回当前的Sprite
/// <summary>
/// Image's texture comes from the UnityEngine.Image.
/// </summary>
public override Texture mainTexture
{
get
{
return sprite == null ? s_WhiteTexture : sprite.texture;
}
}
#endregion
#region EditorDisplayParameter
#if UNITY_EDITOR
[InvokeMethod("EditorDisplayParameter")]
public string invoke;
/// <summary>
/// OnDisplayPath
/// </summary>
/// <param name="_position">位置</param>
/// <param name="_property">属性</param>
/// <param name="_label">标签</param>
/// <returns>高度</returns>
float EditorDisplayParameter(Rect _position, SerializedProperty _property, GUIContent _label)
{
float y = _position.y;
_position.height = 16;
if (mGraphicMasks != null && mGraphicMasks.Count > 0)
{
EditorGUI.LabelField(_position, "Ignore Graphic");
_position.y += _position.height;
for (int i = 0; i < mGraphicMasks.Count; i++)
{
EditorGUI.ObjectField(_position, (i + 1).PadLeft(mGraphicMasks.Count) + ".", mGraphicMasks[i], typeof(Graphic), true);
_position.y += _position.height;
}
}
return _position.y - y;
}
#endif
#endregion
#region enTriggerEventClassiy 事件触发分类
/// <summary>
/// 事件触发分类
/// </summary>
public enum enTriggerEventClassiy
{
/// <summary>
/// 禁用事件
/// </summary>
[AliasTooltip("禁用事件")]
DisableAll,
/// <summary>
/// 仅禁用遮罩事件
/// </summary>
[AliasTooltip("仅禁用遮罩事件")]
MaskDisable,
/// <summary>
/// 仅开启遮罩事件
/// </summary>
[AliasTooltip("仅开启遮罩事件")]
MaskEnable,
/// <summary>
/// 开启事件
/// </summary>
[AliasTooltip("开启事件")]
OpenAll,
}
#endregion
#region enGraphicMaskClassify 绘制遮罩分类
/// <summary>
/// 绘制遮罩分类
/// </summary>
public enum enGraphicMaskClassify
{
/// <summary>
/// 全屏填充
/// </summary>
[AliasTooltip("全屏填充")]
Scene,
/// <summary>
/// 仅填充遮罩
/// </summary>
[AliasTooltip("仅填充遮罩")]
Mask,
/// <summary>
/// 除遮罩之外都填充
/// </summary>
[AliasTooltip("除遮罩之外都填充")]
ExceptMask,
}
#endregion
#region enGraphicSpriteFillClassify 绘制精灵填充分类
/// <summary>
/// 绘制精灵填充分类
/// </summary>
public enum enGraphicSpriteFillClassify
{
/// <summary>
/// 按所占屏幕比例填充
/// </summary>
[AliasTooltip("按所占屏幕比例填充")]
SceneRatioFill,
/// <summary>
/// 遮罩单独填充
/// </summary>
[AliasTooltip("遮罩单独填充")]
MaskAloneFill,
}
#endregion
#region IsRaycastLocationValid 是否通过Raycast验证
/// <summary>
/// 是否通过Raycast验证
/// </summary>
/// <param name="_screenPoint">屏幕坐标</param>
/// <param name="_eventCamera">检测相机</param>
/// <returns>True:检测到有效目标【事件停止传递】,False:未检测到有效目标【事件继续传递】</returns>
public bool IsRaycastLocationValid(Vector2 _screenPoint, Camera _eventCamera)
{
bool isStopEvent = false;
switch (triggerEventClassify)
{
case enTriggerEventClassiy.DisableAll:
isStopEvent = true;
break;
case enTriggerEventClassiy.MaskDisable:
isStopEvent = IsPointInAnyMask(_screenPoint, _eventCamera);
break;
case enTriggerEventClassiy.MaskEnable:
isStopEvent = !IsPointInAnyMask(_screenPoint, _eventCamera);
break;
case enTriggerEventClassiy.OpenAll:
isStopEvent = false;
break;
}
return isStopEvent;
}
/// <summary>
/// Point是否在任意Mask上
/// </summary>
/// <param name="_screenPoint">屏幕坐标</param>
/// <param name="_eventCamera">检测相机</param>
/// <returns>True:是,False:否</returns>
bool IsPointInAnyMask(Vector2 _screenPoint, Camera _eventCamera)
{
bool isPointInAnyMask = false;
if (mGraphicMasks != null && mGraphicMasks.Count > 0)
{
foreach (Graphic g in mGraphicMasks)
{
if (RectTransformUtility.RectangleContainsScreenPoint(g.rectTransform, _screenPoint, _eventCamera))
{
isPointInAnyMask = true;
break;
}
}
}
return isPointInAnyMask;
}
#endregion
#region graphicMasks 当前GraphicMask组
/// <summary>
/// 当前GraphicMask组
/// </summary>
protected List<Graphic> graphicMasks { get { return mGraphicMasks; } }
#endregion
#region AddGraphicMask 添加遮罩Graphic
/// <summary>
/// Graphic组
/// </summary>
List<Graphic> mGraphicMasks = new List<Graphic>();
/// <summary>
/// 添加Graphic遮罩
/// </summary>
/// <param name="_mask">遮罩</param>
public void AddGraphicMask(Graphic _mask)
{
if (!mGraphicMasks.Contains(_mask))
{
mGraphicMasks.Add(_mask);
RegisterDirty(_mask);
SetVerticesDirty();
}
}
#endregion
#region RemoveGraphicMask 移除Graphic遮罩
/// <summary>
/// 移除Graphic遮罩
/// </summary>
/// <param name="_mask">遮罩</param>
public void RemoveGraphicMask(Graphic _mask)
{
mGraphicMasks.Remove(_mask);
UnregisterDirty(_mask);
SetVerticesDirty();
}
#endregion
#region RegisterDirty 注册Dirty事件
/// <summary>
/// 注册Dirty事件
/// </summary>
/// <param name="_graphic">绘制</param>
void RegisterDirty(Graphic _graphic)
{
_graphic.RegisterDirtyLayoutCallback(DirtyAction);
_graphic.RegisterDirtyMaterialCallback(DirtyAction);
_graphic.RegisterDirtyVerticesCallback(DirtyAction);
}
/// <summary>
/// Dirty动作
/// </summary>
void DirtyAction()
{
SetVerticesDirty();
}
/// <summary>
/// 注册Dirty事件
/// </summary>
/// <param name="_graphic">绘制</param>
void UnregisterDirty(Graphic _graphic)
{
_graphic.UnregisterDirtyLayoutCallback(DirtyAction);
_graphic.UnregisterDirtyMaterialCallback(DirtyAction);
_graphic.UnregisterDirtyVerticesCallback(DirtyAction);
}
#endregion
#region IsChangeGraphicMaskVariable 绘制遮罩变量是否有变更
/// <summary>
/// 最后绘制的graphic遮罩
/// </summary>
Dictionary<int, GraphicVariable> mLastDrawGraphicMaskMaping = new Dictionary<int, GraphicVariable>();
/// <summary>
/// 绘制遮罩变量是否有变更
/// </summary>
/// <param name="_masks">绘制遮罩</param>
/// <returns>True:有变更,False:无变更</returns>
bool IsChangeGraphicMaskVariable(List<Graphic> _masks)
{
bool isAnyMaskChange = mLastDrawGraphicMaskMaping.Count != _masks.Count;
int gid = 0;
if (!isAnyMaskChange)
{
//如果个数相同,则看是不是与最后保存的是同样的Graphic
foreach (Graphic g in _masks)
{
gid = g.gameObject.GetInstanceID();
isAnyMaskChange |= !mLastDrawGraphicMaskMaping.ContainsKey(gid);
if (isAnyMaskChange)
{
break;
}
}
}
if (!isAnyMaskChange)
{
//如果与保存的是同样的Graphic,则看每个Graphic的参数是否有不同
foreach (Graphic g in _masks)
{
isAnyMaskChange |= mLastDrawGraphicMaskMaping[g.gameObject.GetInstanceID()].isDifferent(g);
if (isAnyMaskChange)
{
break;
}
}
}
else
{
mLastDrawGraphicMaskMaping.Clear();
foreach (Graphic g in _masks)
{
mLastDrawGraphicMaskMaping.Add(g.gameObject.GetInstanceID(), new GraphicVariable(g));
}
}
return isAnyMaskChange;
}
/// <summary>
/// Graphic变量
/// </summary>
class GraphicVariable
{
/// <summary>
/// anchoredPosition
/// </summary>
Vector2 mAnchoredPosition = Vector2.zero;
/// <summary>
/// anchoredPosition3D
/// </summary>
Vector3 mAnchoredPosition3D = Vector3.zero;
/// <summary>
/// anchorMin
/// </summary>
Vector2 mAnchorMin = Vector2.zero;
/// <summary>
/// anchorMax
/// </summary>
Vector2 mAnchorMax = Vector2.zero;
/// <summary>
/// offsetMin
/// </summary>
Vector2 mOffsetMin = Vector2.zero;
/// <summary>
/// offsetMax
/// </summary>
Vector2 mOffsetMax = Vector2.zero;
/// <summary>
/// pivot
/// </summary>
Vector2 mPivot = Vector2.zero;
/// <summary>
/// sizeDelta
/// </summary>
Vector2 mSizeDelta = Vector2.zero;
/// <summary>
/// localEulerAngles
/// </summary>
Vector3 mLocalEulerAngles = Vector3.zero;
/// <summary>
/// localScale
/// </summary>
Vector3 mLocalScale = Vector3.zero;
/// <summary>
/// Graphic变量
/// </summary>
/// <param name="_g">Graphic</param>
public GraphicVariable(Graphic _g)
{
SetVariable(_g);
}
/// <summary>
/// 设置变量
/// </summary>
/// <param name="_g">Graphic</param>
void SetVariable(Graphic _g)
{
RectTransform rt = (RectTransform)_g.gameObject.transform;
mAnchoredPosition = rt.anchoredPosition;
mAnchoredPosition3D = rt.anchoredPosition3D;
mAnchorMin = rt.anchorMin;
mAnchorMax = rt.anchorMax;
mOffsetMin = rt.offsetMin;
mOffsetMax = rt.offsetMax;
mPivot = rt.pivot;
mSizeDelta = rt.sizeDelta;
mLocalScale = rt.localScale;
mLocalEulerAngles = rt.localEulerAngles;
}
/// <summary>
/// 是否与指定Graphic不同
/// </summary>
/// <param name="_g">Graphic</param>
/// <returns></returns>
public bool isDifferent(Graphic _g)
{
RectTransform rt = (RectTransform)_g.gameObject.transform;
bool isNot = mAnchoredPosition != rt.anchoredPosition ||
mAnchoredPosition3D != rt.anchoredPosition3D ||
mAnchorMin != rt.anchorMin ||
mAnchorMax != rt.anchorMax ||
mOffsetMin != rt.offsetMin ||
mOffsetMax != rt.offsetMax ||
mPivot != rt.pivot ||
mSizeDelta != rt.sizeDelta ||
mLocalScale != rt.localScale ||
mLocalEulerAngles != rt.localEulerAngles;
if (isNot)
{
SetVariable(_g);
}
return isNot;
}
}
#endregion
#region LateUpdate
/// <summary>
/// LateUpdate
/// </summary>
private void LateUpdate()
{
if (IsChangeGraphicMaskVariable(mGraphicMasks))
{
DirtyAction();
}
}
#endregion
}
UIGraphicMaskShader.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 绘制遮罩【Shader版】
/// 依赖项
/// 1.UIGraphicMaskShader.shader
/// </summary>
[AddComponentMenu("Game/UI/GraphicMask/Shader")]
public class UIGraphicMaskShader : AbsUIGraphicMask
{
#region OnPopulateMesh
/// <summary>
/// 空图
/// </summary>
Texture2D mTextureClear = null;
/// <summary>
/// 最大图数量
/// </summary>
int mMaxTextureNum = 0;
/// <summary>
/// Fill the vertex buffer data.
/// </summary>
/// <param name="_vh">顶点Helper</param>
protected override void OnPopulateMesh(VertexHelper _vh)
{
if (graphicMasks != null && graphicMasks.Count > 0)
{
Rect sceneRect = this.GraphicLocalRectForVertexHelper();
switch (graphicClassify)
{
case enGraphicMaskClassify.Scene:
OnFillScene(sceneRect, graphicMasks);
break;
case enGraphicMaskClassify.Mask:
OnFillMask(sceneRect, graphicMasks);
break;
case enGraphicMaskClassify.ExceptMask:
OnFillExceptMask(sceneRect, graphicMasks);
break;
}
mMaxTextureNum = Mathf.Max(mMaxTextureNum, graphicMasks.Count);
}
else
{
if (mTextureClear == null)
{
mTextureClear = new Texture2D(1, 1, TextureFormat.ARGB32, false, false);
mTextureClear.SetPixel(0, 0, Color.clear);
mTextureClear.Apply();
}
for (int i = 0; i < mMaxTextureNum; i++)
{
material.SetTexture("_GraphicTex" + i, mTextureClear);
}
}
base.OnPopulateMesh(_vh);
}
#endregion
#region OnFillScene 全屏填充
/// <summary>
/// 全屏填充
/// </summary>
/// <param name="_sceneRect">屏幕矩形</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillScene(Rect _sceneRect,List<Graphic> _masks)
{
//_vh.Clear();
//List<UIVertex> vertexs = new List<UIVertex>();
//Vector3[] fourCorners = null;
//Vector4 uv = (sprite != null) ? UnityEngine.Sprites.DataUtility.GetOuterUV(sprite) : Vector4.zero;
//this.GraphicLocalCornersForVertexHelper(out fourCorners);
//vertexs.Add(new UIVertex() { position = fourCorners[0], color = color, uv0 = new Vector2(uv.x, uv.y) });
//vertexs.Add(new UIVertex() { position = fourCorners[1], color = color, uv0 = new Vector2(uv.x, uv.w) });
//vertexs.Add(new UIVertex() { position = fourCorners[2], color = color, uv0 = new Vector2(uv.z, uv.w) });
//vertexs.Add(new UIVertex() { position = fourCorners[3], color = color, uv0 = new Vector2(uv.z, uv.y) });
//_vh.AddUIVertexQuad(vertexs.ToArray());
OnFillExceptMask(_sceneRect, _masks);
}
#endregion
#region OnFillMask 仅填充遮罩
//x=>u最小值,z=>u最大值 ,y=>v最小值,y=>v最大值
//v
//| xw---------zw
//| | |
//| xy----------zy
//------------------------u
static readonly Vector4 uvDefault = new Vector4(0, 0, 1, 1);
/// <summary>
/// 仅填充遮罩
/// </summary>
/// <param name="_sceneRect">屏幕矩形</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillMask(Rect _sceneRect, List<Graphic> _masks)
{
OnFillExceptMask(_sceneRect,_masks);
}
#endregion
#region OnFillExceptMask 除遮罩之外都填充
/// <summary>
/// 除遮罩之外都填充
/// </summary>
/// <param name="_sceneRect">屏幕矩形</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillExceptMask(Rect _sceneRect, List<Graphic> _masks)
{
Sprite sprite = null;
Image image = null;
Vector4 spriteUV = Vector4.zero;
Vector4 spriteUVClip = Vector4.zero;
Vector4 spriteBorder = Vector4.zero;
Vector4 rectUV = Vector4.zero;
Vector4 sceneGraphicWh = Vector4.zero;
Vector4 graphicRectSlicedBorder = Vector4.zero;
Vector4 graphicUvSlicedBorderRatio = Vector4.zero;
Vector2 rectMin = Vector2.zero;
Vector2 rectMax = Vector2.zero;
bool isGraphicSliced = false;
sceneGraphicWh.x = _sceneRect.size.x;
sceneGraphicWh.y = _sceneRect.size.y;
Rect maskRect = new Rect();
for (int i = 0; i < _masks.Count; i++)
{
spriteUV = uvDefault;
spriteUVClip = uvDefault;
spriteBorder = Vector4.zero;
isGraphicSliced = false;
maskRect = _masks[i].GraphicLocalRectForVertexHelper();
sceneGraphicWh.z = maskRect.size.x;
sceneGraphicWh.w = maskRect.size.y;
rectMin = Rect.PointToNormalized(_sceneRect, maskRect.min);
rectMax = Rect.PointToNormalized(_sceneRect, maskRect.max);
rectUV.Set(rectMin.x, rectMin.y, rectMax.x, rectMax.y);
if (!Geometry2DUtility.IsPointInRect(maskRect.min, _sceneRect))
{
if (rectMin.x == 0)
{
spriteUVClip.x = Mathf.InverseLerp(maskRect.min.x, maskRect.max.x, _sceneRect.min.x);
}
if (rectMin.y == 0)
{
spriteUVClip.y = Mathf.InverseLerp(maskRect.min.y, maskRect.max.y, _sceneRect.min.y);
}
}
if (_masks[i] is Image)
{
image = (Image)_masks[i];
sprite = image.overrideSprite;
if (sprite != null)
{
spriteUV = UnityEngine.Sprites.DataUtility.GetOuterUV(sprite);
graphicRectSlicedBorder.x = sprite.border.x / _masks[i].GetPixelAdjustedRect().width * 0.5f;
graphicRectSlicedBorder.y = sprite.border.y / _masks[i].GetPixelAdjustedRect().height * 0.5f;
graphicRectSlicedBorder.z = sprite.border.z / _masks[i].GetPixelAdjustedRect().width * 0.5f;
graphicRectSlicedBorder.w = sprite.border.w / _masks[i].GetPixelAdjustedRect().height * 0.5f;
graphicRectSlicedBorder.z = 1 - graphicRectSlicedBorder.z;
graphicRectSlicedBorder.w = 1 - graphicRectSlicedBorder.w;
graphicUvSlicedBorderRatio.x = sprite.border.x / sprite.textureRect.width;
graphicUvSlicedBorderRatio.y = sprite.border.y / sprite.textureRect.height;
graphicUvSlicedBorderRatio.z = sprite.border.z / sprite.textureRect.width;
graphicUvSlicedBorderRatio.w = sprite.border.w / sprite.textureRect.height;
}
switch (image.type)
{
case Image.Type.Sliced:
case Image.Type.Tiled:
isGraphicSliced = true;
break;
}
}
material.SetTexture("_GraphicTex" + i, _masks[i].mainTexture);
material.SetVector("_GraphicUvSlicedBorderRatio" + i, graphicUvSlicedBorderRatio);
material.SetVector("_SceneGraphicWh" + i, sceneGraphicWh);
material.SetVector("_GraphicUvMinMax" + i, spriteUV);
material.SetVector("_GraphicUvMinMaxClip" + i, spriteUVClip);
material.SetVector("_GraphicUvMaskForScene" + i, rectUV);
material.SetInt("_isGraphicSliced" + i, isGraphicSliced ? 1 : 0);
material.SetVector("_GraphicRectSlicedBorder" + i, graphicRectSlicedBorder);
}
}
#endregion
}
UIGraphicMaskShader.shader
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Game/UI/GraphicMask/Shader"
{
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
[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
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.worldPosition = IN.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
sampler2D _MaskTex;
//遮罩要支持多少个,这里就添加多少个,建议最多不超过6个
//遮罩
sampler2D _GraphicTex0;
//屏幕与遮罩长宽
float4 _SceneGraphicWh0;
//遮罩最小最大uv坐标
float4 _GraphicUvMinMax0;
//遮罩最小最大uv坐标裁剪
float4 _GraphicUvMinMaxClip0;
//遮罩矩形(0,0)(1,1)相对于屏幕uv坐标
float4 _GraphicUvMaskForScene0;
//遮罩Sliced绘制
int _isGraphicSliced0;
//遮罩矩形Sliced Border
float4 _GraphicRectSlicedBorder0;
//遮罩图Sliced Border缩放比率
float4 _GraphicUvSlicedBorderRatio0;
//遮罩
sampler2D _GraphicTex1;
//屏幕与遮罩长宽
float4 _SceneGraphicWh1;
//遮罩最小最大uv坐标
float4 _GraphicUvMinMax1;
//遮罩最小最大uv坐标裁剪
float4 _GraphicUvMinMaxClip1;
//遮罩矩形(0,0)(1,1)相对于屏幕uv坐标
float4 _GraphicUvMaskForScene1;
//遮罩Sliced绘制
int _isGraphicSliced1;
//遮罩矩形Sliced Border
float4 _GraphicRectSlicedBorder1;
//遮罩图Sliced Border缩放比率
float4 _GraphicUvSlicedBorderRatio1;
/*
x=>u最小值,z=>u最大值 ,y=>v最小值,y=>v最大值
v
| xw---------zw
| | |
| xy----------zy
------------------------u
*/
//遮罩填充
fixed fragFillMask(half2 _texcoord,sampler2D _graphicTex,float4 _sceneGraphicWh,
float4 _graphicUvMinMax,float4 _graphicUvMinMaxClip,float4 _graphicUvMaskForScene,
int _isGraphicSliced,float4 _GraphicRectSlicedBorder,float4 _GraphicUvSlicedBorderRatio)
{
fixed alpha = 1;
//屏幕点
half2 texuv = _texcoord.xy;
//如果点在要绘制的遮罩框内才进行像素计算
if(texuv.x >= _graphicUvMaskForScene.x && texuv.x <= _graphicUvMaskForScene.z
&& texuv.y >= _graphicUvMaskForScene.y && texuv.y <= _graphicUvMaskForScene.w)
{
//长宽比
float2 ratiouv = float2(_sceneGraphicWh.x/_sceneGraphicWh.z,_sceneGraphicWh.y/_sceneGraphicWh.w);
//矩形参数
float4 rectParams = float4(_graphicUvMaskForScene.x,_graphicUvMaskForScene.y, _graphicUvMaskForScene.z - _graphicUvMaskForScene.x,_graphicUvMaskForScene.w - _graphicUvMaskForScene.y);
//遮罩图参数
float4 texParams= float4(_graphicUvMinMax.x,_graphicUvMinMax.y,_graphicUvMinMax.z - _graphicUvMinMax.x,_graphicUvMinMax.w - _graphicUvMinMax.y);
//遮罩颜色
float4 graphicColor = float4(1,1,1,1);
if(_isGraphicSliced == 1)
{
//_GraphicUvSlicedBorder 九宫uv值,判定当前uv在九宫的哪一格,按当前格子的缩放进行uv读取
//转换到遮罩(0,0)坐标
texuv -=_graphicUvMaskForScene.xy;
//由屏幕坐标系转到遮罩坐标系
texuv *=ratiouv;
texuv +=_graphicUvMinMaxClip.xy;
if(texuv.x > _GraphicRectSlicedBorder.x && texuv.x < _GraphicRectSlicedBorder.z && texuv.y > _GraphicRectSlicedBorder.y && texuv.y < _GraphicRectSlicedBorder.w)
{
graphicColor.a = 1;
}
else
{
//左下角
if(texuv.x< _GraphicRectSlicedBorder.x &&
texuv.y <_GraphicRectSlicedBorder.y)
{
//坐标转换到左下角坐标系(0,1)
texuv /= _GraphicRectSlicedBorder.xy;
//坐标转换到Texture坐标系
texuv *= _GraphicUvSlicedBorderRatio.xy;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//左上角
else if(texuv.x< _GraphicRectSlicedBorder.x &&
texuv.y >_GraphicRectSlicedBorder.w)
{
//相对于左上角为原点的坐标
texuv.y =1 - texuv.y;
//坐标转换到左上角坐标系(0,1)
texuv.x /= _GraphicRectSlicedBorder.x;
texuv.y /= (1 - _GraphicRectSlicedBorder.w);
//坐标转换到Texture坐标系
texuv *= _GraphicUvSlicedBorderRatio.xw;
texuv.y = 1 - texuv.y;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//右上角
else if(texuv.x > _GraphicRectSlicedBorder.z &&
texuv.y >_GraphicRectSlicedBorder.w)
{
//相对于左上角为原点的坐标
texuv =1 - texuv;
//坐标转换到左上角坐标系(0,1)
texuv /= (1 -_GraphicRectSlicedBorder.zw);
//坐标转换到Texture坐标系
texuv *= _GraphicUvSlicedBorderRatio.zw;
texuv = 1 - texuv;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//右下角
else if(texuv.x > _GraphicRectSlicedBorder.z &&
texuv.y < _GraphicRectSlicedBorder.y)
{
//相对于左上角为原点的坐标
texuv.x =1 - texuv.x;
//坐标转换到左上角坐标系(0,1)
texuv.x /= (1 -_GraphicRectSlicedBorder.z);
texuv.y /= _GraphicRectSlicedBorder.y;
//坐标转换到Texture坐标系
texuv *= _GraphicUvSlicedBorderRatio.zy;
texuv.x = 1 - texuv.x;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//左边
else if(texuv.x< _GraphicRectSlicedBorder.x &&
texuv.y > _GraphicRectSlicedBorder.y && texuv.y < _GraphicRectSlicedBorder.w)
{
//相对于左边左下角为原点的坐标
texuv.y -= _GraphicRectSlicedBorder.y;
//坐标转换到左下角坐标系(0,1)
texuv.x /= _GraphicRectSlicedBorder.x;
texuv.y /= _GraphicRectSlicedBorder.w - _GraphicRectSlicedBorder.y;
//坐标转换到Texture坐标系
texuv.x *= _GraphicUvSlicedBorderRatio.x;
texuv.y *= 1 - _GraphicUvSlicedBorderRatio.w - _GraphicUvSlicedBorderRatio.y;
//坐标补上左下角的y值
texuv.y += _GraphicUvSlicedBorderRatio.y;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//右边
else if(texuv.x > _GraphicRectSlicedBorder.z &&
texuv.y > _GraphicRectSlicedBorder.y && texuv.y < _GraphicRectSlicedBorder.w)
{
//相对于右边右上角为原点的坐标
texuv = 1 - texuv;
texuv.y -= 1-_GraphicRectSlicedBorder.w;
//坐标转换到右上角坐标系(0,1)
texuv.x /= 1 - _GraphicRectSlicedBorder.z;
texuv.y /= _GraphicRectSlicedBorder.w - _GraphicRectSlicedBorder.y;
//坐标转换到Texture坐标系
texuv.x *= _GraphicUvSlicedBorderRatio.z;
texuv.y *= 1 - _GraphicUvSlicedBorderRatio.w - _GraphicUvSlicedBorderRatio.y;
//坐标补上左下角的y值
texuv.y += _GraphicUvSlicedBorderRatio.w;
texuv = 1-texuv;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//下边
else if(texuv.x > _GraphicRectSlicedBorder.x && texuv.x < _GraphicRectSlicedBorder.z &&
texuv.y < _GraphicRectSlicedBorder.y)
{
//相对于下边左下角为原点的坐标
texuv.x -= _GraphicRectSlicedBorder.x;
//坐标转换到左下角坐标系(0,1)
texuv.x /=_GraphicRectSlicedBorder.z - _GraphicRectSlicedBorder.x;
texuv.y /= _GraphicRectSlicedBorder.y;
//坐标转换到Texture坐标系
texuv.x *= 1 - _GraphicUvSlicedBorderRatio.z - _GraphicUvSlicedBorderRatio.x;
texuv.y *= _GraphicUvSlicedBorderRatio.y;
//坐标补上左下角的x值
texuv.x += _GraphicUvSlicedBorderRatio.x;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
//上边
else if(texuv.x > _GraphicRectSlicedBorder.x && texuv.x < _GraphicRectSlicedBorder.z &&
texuv.y > _GraphicRectSlicedBorder.w)
{
//相对于上边右上角为原点的坐标
texuv = 1 - texuv;
texuv.x -= 1- _GraphicRectSlicedBorder.z;
texuv.y -= 1-_GraphicRectSlicedBorder.w;
//坐标转换到右上角坐标系(0,1)
texuv.x /= _GraphicRectSlicedBorder.z - _GraphicRectSlicedBorder.x;
texuv.y /= 1 -_GraphicRectSlicedBorder.w;
//坐标转换到Texture坐标系
texuv.x *= 1 - _GraphicUvSlicedBorderRatio.z - _GraphicUvSlicedBorderRatio.x;
texuv.y *= _GraphicUvSlicedBorderRatio.w;
//坐标补上右上角的xy值
texuv.x += _GraphicUvSlicedBorderRatio.z;
texuv.y += _GraphicUvSlicedBorderRatio.w;
texuv = 1-texuv;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
}
}
alpha = 1-graphicColor.a;
}
else
{
//转换到遮罩(0,0)坐标
texuv -=_graphicUvMaskForScene.xy;
//由屏幕坐标系转到遮罩坐标系
texuv *=ratiouv;
//遮罩在屏幕的矩形裁剪
texuv +=_graphicUvMinMaxClip.xy;
//遮罩坐标系转到Texture坐标系
texuv = texuv * texParams.zw + texParams.xy;
//遮罩在屏幕的矩形裁剪
//texuv +=_graphicUvMinMaxClip.xy * texParams.zw;
graphicColor = tex2D(_graphicTex,texuv);
alpha = 1-graphicColor.a;
}
}
return alpha;
}
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
//遮罩颜色混合
fixed alpha = fragFillMask(IN.texcoord,_GraphicTex0,_SceneGraphicWh0,_GraphicUvMinMax0,_GraphicUvMinMaxClip0,_GraphicUvMaskForScene0,_isGraphicSliced0,_GraphicRectSlicedBorder0,_GraphicUvSlicedBorderRatio0);
alpha *= fragFillMask(IN.texcoord,_GraphicTex1,_SceneGraphicWh1,_GraphicUvMinMax1,_GraphicUvMinMaxClip1,_GraphicUvMaskForScene1,_isGraphicSliced1,_GraphicRectSlicedBorder1,_GraphicUvSlicedBorderRatio1);
color.a *=alpha;
//裁剪
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
float4 mask = tex2D(_MaskTex,IN.texcoord);
return color;
}
ENDCG
}
}
}
UIGraphicMaskVertex.cs
using System;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 绘制遮罩【顶点版】
/// 依赖项
/// 1.UIGraphicMaskVertex.shader
/// </summary>
[AddComponentMenu("Game/UI/GraphicMask/Vertex")]
public class UIGraphicMaskVertex : AbsUIGraphicMask
{
#region OnPopulateMesh
/// <summary>
/// Fill the vertex buffer data.
/// </summary>
/// <param name="_vh">顶点Helper</param>
protected override void OnPopulateMesh(VertexHelper _vh)
{
if (graphicMasks != null && graphicMasks.Count > 0)
{
switch (graphicClassify)
{
case enGraphicMaskClassify.Scene:
OnFillScene(_vh, graphicMasks);
break;
case enGraphicMaskClassify.Mask:
OnFillMask(_vh, graphicMasks);
break;
case enGraphicMaskClassify.ExceptMask:
OnFillExceptMask(_vh, graphicMasks);
break;
}
}
else
{
base.OnPopulateMesh(_vh);
}
}
#endregion
#region OnFillScene 全屏填充
/// <summary>
/// 全屏填充
/// </summary>
/// <param name="_vh">顶点Helper</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillScene(VertexHelper _vh, List<Graphic> _masks)
{
_vh.Clear();
List<UIVertex> vertexs = new List<UIVertex>();
Vector3[] fourCorners = null;
Vector4 uv = (sprite != null) ? UnityEngine.Sprites.DataUtility.GetOuterUV(sprite) : Vector4.zero;
this.GraphicLocalCornersForVertexHelper(out fourCorners);
vertexs.Add(new UIVertex() { position = fourCorners[0], color = color, uv0 = new Vector2(uv.x, uv.y) });
vertexs.Add(new UIVertex() { position = fourCorners[1], color = color, uv0 = new Vector2(uv.x, uv.w) });
vertexs.Add(new UIVertex() { position = fourCorners[2], color = color, uv0 = new Vector2(uv.z, uv.w) });
vertexs.Add(new UIVertex() { position = fourCorners[3], color = color, uv0 = new Vector2(uv.z, uv.y) });
_vh.AddUIVertexQuad(vertexs.ToArray());
}
#endregion
#region OnFillMask 仅填充遮罩
/// <summary>
/// 仅填充遮罩
/// </summary>
/// <param name="_vh">顶点Helper</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillMask(VertexHelper _vh, List<Graphic> _masks)
{
_vh.Clear();
List<UIVertex> vertexs = new List<UIVertex>();
Vector3[] fourCorners = null;
GraphicRect2D sceneRect = new GraphicRect2D(-1, this);
Vector4 uv = (sprite != null) ? UnityEngine.Sprites.DataUtility.GetOuterUV(sprite) : Vector4.zero;
foreach (Graphic g in _masks)
{
g.GraphicLocalCornersForVertexHelper(out fourCorners);
vertexs = new List<UIVertex>();
switch (graphicSpriteFillClassify)
{
case enGraphicSpriteFillClassify.SceneRatioFill:
fourCorners[0].x = Mathf.Clamp(fourCorners[0].x, sceneRect.rect.xMin, sceneRect.rect.xMax);
fourCorners[0].y = Mathf.Clamp(fourCorners[0].y, sceneRect.rect.yMin, sceneRect.rect.yMax);
vertexs.Add(new UIVertex() { position = fourCorners[0], color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, fourCorners[0]) });
fourCorners[1].x = Mathf.Clamp(fourCorners[1].x, sceneRect.rect.xMin, sceneRect.rect.xMax);
fourCorners[1].y = Mathf.Clamp(fourCorners[1].y, sceneRect.rect.yMin, sceneRect.rect.yMax);
vertexs.Add(new UIVertex() { position = fourCorners[1], color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, fourCorners[1]) });
fourCorners[2].x = Mathf.Clamp(fourCorners[2].x, sceneRect.rect.xMin, sceneRect.rect.xMax);
fourCorners[2].y = Mathf.Clamp(fourCorners[2].y, sceneRect.rect.yMin, sceneRect.rect.yMax);
vertexs.Add(new UIVertex() { position = fourCorners[2], color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, fourCorners[2]) });
fourCorners[3].x = Mathf.Clamp(fourCorners[3].x, sceneRect.rect.xMin, sceneRect.rect.xMax);
fourCorners[3].y = Mathf.Clamp(fourCorners[3].y, sceneRect.rect.yMin, sceneRect.rect.yMax);
vertexs.Add(new UIVertex() { position = fourCorners[3], color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, fourCorners[3]) });
break;
case enGraphicSpriteFillClassify.MaskAloneFill:
vertexs.Add(new UIVertex() { position = fourCorners[0], color = color, uv0 = new Vector2(uv.x, uv.y) });
vertexs.Add(new UIVertex() { position = fourCorners[1], color = color, uv0 = new Vector2(uv.x, uv.w) });
vertexs.Add(new UIVertex() { position = fourCorners[2], color = color, uv0 = new Vector2(uv.z, uv.w) });
vertexs.Add(new UIVertex() { position = fourCorners[3], color = color, uv0 = new Vector2(uv.z, uv.y) });
break;
}
_vh.AddUIVertexQuad(vertexs.ToArray());
}
}
#endregion
#region OnFillExceptMask 除遮罩之外都填充
/// <summary>
/// 三角剖分结果
/// </summary>
PolygonDelaunayResult2D mDelaunayResult = null;
/// <summary>
/// 除遮罩之外都填充
/// </summary>
/// <param name="_vh">顶点Helper</param>
/// <param name="_masks">绘制组</param>
protected virtual void OnFillExceptMask(VertexHelper _vh, List<Graphic> _masks)
{
#region 屏幕矩形
GraphicRect2D sceneRect = new GraphicRect2D(-1, this);
#endregion
#region 遮罩矩形
List<GraphicRect2D> maskRect = new List<GraphicRect2D>();
GraphicRect2D rect;
for (int i = 0; i < _masks.Count; i++)
{
rect = new GraphicRect2D(i, _masks[i]);
maskRect.Add(rect);
}
//求两矩形填充点和矩形在屏幕上的填充点
List<Vector3> useVertexs = new List<Vector3>();
List<Vector3> tempVertexs = new List<Vector3>();
List<Vector3> validateVertexs = new List<Vector3>();
if (maskRect.Count <= 1)
{
tempVertexs = maskRect[0].IntersectionFill(sceneRect);
if (tempVertexs.Count > 0)
{
validateVertexs.AddRange(tempVertexs);
}
}
else
{
foreach (GraphicRect2D a in maskRect)
{
foreach (GraphicRect2D b in maskRect)
{
tempVertexs = a.IntersectionFill(b);
if (tempVertexs.Count > 0)
{
validateVertexs.AddRange(tempVertexs);
}
}
}
}
#endregion
validateVertexs.InsertRange(0, sceneRect.corners);
#region 清理重复的点
List<int> hasPoint = new List<int>();
int pk = 0;
Vector3 tp = Vector3.zero;
foreach (Vector3 cv in validateVertexs)
{
//顶点不超过屏幕
tp.x = cv.x;
tp.x = Mathf.Max(tp.x, sceneRect.rect.xMin);
tp.x = Mathf.Min(tp.x, sceneRect.rect.xMax);
tp.y = cv.y;
tp.y = Mathf.Max(tp.y, sceneRect.rect.yMin);
tp.y = Mathf.Min(tp.y, sceneRect.rect.yMax);
//过滤重复点
pk = (tp.x.ToString() + tp.y.ToString()).UniqueHashCode();
if (!hasPoint.Contains(pk))
{
hasPoint.Add(pk);
useVertexs.Add(tp);
}
}
#endregion
mDelaunayResult = Polygon2DUtility.BuilderDelaunay2D(useVertexs, new List<int>() { 0, 1, 2, 3 });
#region 绘制三角面
_vh.Clear();
Vector4 uv = (sprite != null) ? UnityEngine.Sprites.DataUtility.GetOuterUV(sprite) : Vector4.zero;
UIVertex[] vertexs = new UIVertex[mDelaunayResult.vertexs.Count];
List<int> vertexIndexs = new List<int>();
foreach (PolygonPoint2D p in mDelaunayResult.vertexs)
{
vertexs[p.index] = new UIVertex() { position = p.position, color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, p.position) };
}
bool isInAnyRect = false;
foreach (PolygonTriangle2D tri in mDelaunayResult.delaunayTriangle)
{
foreach (GraphicRect2D r in maskRect)
{
isInAnyRect =
Geometry2DUtility.IsPointInRect(tri.vertexs[0].position, r.rect) &&
Geometry2DUtility.IsPointInRect(tri.vertexs[1].position, r.rect) &&
Geometry2DUtility.IsPointInRect(tri.vertexs[2].position, r.rect);
if (isInAnyRect)
{
break;
}
}
if (!isInAnyRect)
{
vertexIndexs.Add(tri.vertexs[0].index);
vertexIndexs.Add(tri.vertexs[1].index);
vertexIndexs.Add(tri.vertexs[2].index);
}
}
_vh.AddUIVertexStream(new List<UIVertex>(vertexs), vertexIndexs);
#endregion
}
/// <summary>
/// Graphic矩形2D
/// </summary>
class GraphicRect2D
{
/// <summary>
/// 索引
/// </summary>
public int index { get; private set; }
/// <summary>
/// 四角
/// </summary>
public Vector3[] corners { get; private set; }
/// <summary>
/// 矩形
/// </summary>
public Rect rect { get; private set; }
/// <summary>
/// 包围盒
/// </summary>
public Bounds bounds { get; private set; }
/// <summary>
/// 矩形四角
/// </summary>
/// <param name="_index">矩形索引</param>
/// <param name="_graphic">Graphic</param>
public GraphicRect2D(int _index, Graphic _graphic)
{
index = _index;
Vector3[] cns = null;
rect = _graphic.GraphicLocalRectForVertexHelper(ref cns);
bounds = rect.TransRectToBounds();
corners = cns;
}
/// <summary>
/// 求矩形B在当前矩形上的投影填充点
/// </summary>
/// <param name="_b">矩形B</param>
/// <returns>填充点</returns>
public List<Vector3> IntersectionFill(GraphicRect2D _b)
{
List<Vector3> vertexs = new List<Vector3>();
if (index != _b.index)
{
Vector3 va = Vector3.zero;
Vector3 vb = Vector3.zero;
Vector3 vp = Vector3.zero;
Vector3 ab = Vector3.zero;
Vector3 ap = Vector3.zero;
Vector3 pab = Vector3.zero;
for (int i = 0; i < corners.Length; i++)
{
va = corners[i];
vertexs.Add(va);
if (i >= corners.Length - 1)
{
vb = corners[0];
}
else
{
vb = corners[i + 1];
}
for (int p = 0; p < _b.corners.Length; p++)
{
vp = _b.corners[p];
ab = vb - va;
ap = vp - va;
//P点在AB上的投影点pab
pab = Vector3.Project(ap, ab);
//如果投影点的方向与ab方向一致,并且ab的长度大于ap的长度,则投影点在ab边上,为矩形交点
if (Vector3.Dot(ab, pab) >= 0 && ab.magnitude >= ap.magnitude)
{
vertexs.Add(va + pab);
}
}
}
}
return vertexs;
}
}
#endregion
#region OnDrawGizmos
#if UNITY_EDITOR
/// <summary>
/// OnDrawGizmos
/// </summary>
void OnDrawGizmos()
{
if (mDelaunayResult != null && mDelaunayResult.vertexs != null)
{
Gizmos.color = Color.yellow;
float raudis = Vector3.Distance(mDelaunayResult.bounds.min, mDelaunayResult.bounds.max) * 0.5f;
foreach (PolygonPoint2D point in mDelaunayResult.vertexs)
{
Gizmos.DrawSphere(point.position, raudis / mDelaunayResult.vertexs.Count * 0.3f);
}
Gizmos.color = Color.yellow;
Gizmos.DrawLine(mDelaunayResult.bounds.min, mDelaunayResult.bounds.max);
Gizmos.color = Color.red;
Gizmos.DrawWireCube(mDelaunayResult.bounds.center, mDelaunayResult.bounds.size);
Gizmos.color = Color.grey;
Gizmos.DrawWireSphere(mDelaunayResult.bounds.center, raudis);
Gizmos.color = Color.green;
Gizmos.DrawSphere(mDelaunayResult.supperTriangle.vertexs[0].position, raudis / mDelaunayResult.vertexs.Count * 0.3f);
Gizmos.DrawSphere(mDelaunayResult.supperTriangle.vertexs[1].position, raudis / mDelaunayResult.vertexs.Count * 0.3f);
Gizmos.DrawSphere(mDelaunayResult.supperTriangle.vertexs[2].position, raudis / mDelaunayResult.vertexs.Count * 0.3f);
Gizmos.DrawLine(mDelaunayResult.supperTriangle.vertexs[0].position, mDelaunayResult.supperTriangle.vertexs[1].position);
Gizmos.DrawLine(mDelaunayResult.supperTriangle.vertexs[1].position, mDelaunayResult.supperTriangle.vertexs[2].position);
Gizmos.DrawLine(mDelaunayResult.supperTriangle.vertexs[2].position, mDelaunayResult.supperTriangle.vertexs[0].position);
Light light = FindObjectOfType<Light>();
if (light != null)
{
light.transform.position = mDelaunayResult.bounds.center;
}
foreach (PolygonTriangle2D tri in mDelaunayResult.delaunayTriangle)
{
Gizmos.color = Color.cyan;
Gizmos.DrawLine(tri.vertexs[0].position, tri.vertexs[1].position);
Gizmos.DrawLine(tri.vertexs[1].position, tri.vertexs[2].position);
Gizmos.DrawLine(tri.vertexs[2].position, tri.vertexs[0].position);
//Gizmos.color = Color.white;
//Gizmos.DrawWireSphere(tri.circumCircleCenter, tri.circumCircleRadius);
}
}
}
#endif
#endregion
}
UIGraphicMaskVertex.shader
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Game/UI/GraphicMask/Vertex"
{
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
[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
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
};
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.worldPosition = IN.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
sampler2D _MaskTex;
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
float4 mask = tex2D(_MaskTex,IN.texcoord);
return color;
}
ENDCG
}
}
}