shader绘制 封闭凹多边形

image

之前工作遇到的需求,需要在UI地图上绘制任务区域的大小,顶点数,形状不固定,有凹多边形存在的可能,并且进一步提出需要描边效果;

凹多变形Mask

判断是否在多边形内部,通过一点任意方向穿过多边形边的次数的奇偶做判断,如果是奇数次,是内部,偶数次,是外部

判断一点沿一方向是否和一线端相交的代码如下,通过把是否相交记录为1 或 -1,最终所有的结果相乘即可获得奇偶结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float2 segVec = segEnd - segStart;   
// 计算行列式,判断射线和线段是否平行
float det = rayDir.x * (-segVec.y) - rayDir.y * (-segVec.x);


if (abs(det) < 0.00001) {
    // 射线和平行线段
    return -1;
}

// 解线性方程组求 t 和 u
float2 diff = segStart - rayOrigin;
float t = (diff.x * (-segVec.y) - diff.y * (-segVec.x)) / det;
float u = (rayDir.x * diff.y - rayDir.y * diff.x) / det;

// 判断 t 和 u 是否在对应的范围内
return (t >= 0.0f && u >= 0.0f && u <= 1.0f) * 2 - 1;

image

image

此实现最多提供了8个顶点,当需要顶点数不必要8个时,没用到的那些顶点设置为Point1一样的位置即可,像下图用了五个顶点的边有交错的五角星也是可以实现的,虽然除了好看,对于区域ui来说没什么意义就是了…

如果想要实心的五角星,需要10个顶点;

项目里用作地图区域,8个顶点足够了,而且8个顶点的计算,指令数也用到最后多了,不能在复杂了。

image

描边Mask

描边需要计算点到每条边的距离·,所有边的结果取最小值

1
2
3
4
5
6
7
//点到线距离
float2 ap = uv - a;
float2 ab = b - a;
float dis = dot(ap, ab)/dot(ab, ab);
dis = saturate(dis);
float2 dir = ap - dis * ab;
return length(dir);

image

混合

image