多边形轮廓等距外扩

基本原理

  1. 逐点顶处理多边形,计算出每个顶点的相邻向量
  2. 舍弃相邻向量共线的顶点(将多边形简化,减少三角网格数量)
  3. 由顶点相邻向量与外扩距离计算出外扩后的新顶点坐标
  4. 由新点顶坐标顺序生成三角形数组,根据新点顶坐标和三角形数组绘制新多边形

细节

多边形三角网格简化:
在这里插入图片描述
凹点与凸点:
在这里插入图片描述

关键步骤

如何计算出外扩后新顶点坐标?
已知点Q为原顶点,现在要求外扩后点P的坐标(外扩距离为D)
1.P = Q + 向量QP
2.由平行四边形法则可知向量QP = 向量QN + 向量QM
3.D = |向量QM| * sinA
4.sinA可由点Q相邻两边向量QA与QB叉乘得到
在这里插入图片描述
归纳总结出计算步骤:
1.计算顶点邻边向量(注意向量方向,外扩与收缩向量方向相反)
2.利用邻边向量(单位化)叉乘得到SinA值
3.判断顶点是凹点还是凸点(邻边向量叉乘得到的法向量与点顶法线同向为凸点否则为凹点)
4.如果SinA值为0,则表示邻边向量共线,可以舍弃该顶点(对多边形顶点做简化操作)
5.凸点:Q = P + d/sinA (Normalize(QN) + Normalize(QM))
凹点:Q = P - d/sinA (Normalize(QN) + Normalize(QM))

核心代码:

    //缩放系数
    public float thickness = 0.5f;
    //目标多边形
    public DrawPolygon TargetPolygon;
    //新生成多边形
    private DrawPolygon NewPolygon;
    //相邻向量集合
    private List<AdjacentVector> list;
    //新多边形点顶集合
    private List<Vector3> vectorList;
    
    //绘制外扩后的多边形
    public void DrawNewPolygon()
    {
        list.Clear();
        vectorList.Clear();
        //获取目标多边形的网格数据
        MeshFilter meshFilter = TargetPolygon.GetComponent<MeshFilter>();
        Mesh mesh = null;
        if (Application.isPlaying)
            mesh = meshFilter.mesh;
        else
            mesh = meshFilter.sharedMesh;
        //点顶数据
        Vector3[] vertices = mesh.vertices;
        Vector3[] normal = mesh.normals;

        //所有点顶做差求相邻向量集合
        for(int i=0;i<vertices.Length;i++)
        {
            //计算相邻点顶向量
            Vector3 v1 = vertices[i] - vertices[i == vertices.Length - 1 ? 0 : i + 1];
            Vector3 v2 = vertices[i] - vertices[i == 0 ? vertices.Length - 1 : i - 1];
            //判断是否为凹点(相邻向量的法向量与点顶法向量是否相反)
            bool Cancave = Vector3.Dot(Vector3.Cross(v1, v2), normal[i]) < 0;
            //加入向量集合
            list.Add(new AdjacentVector(v1, v2, Cancave));
        }
        //计算新点顶位置
        for(int i=0;i<vertices.Length;i++)
        {
            Vector3 v1 = list[i].v1;
            Vector3 v2 = list[i].v2;
            //判断相邻向量是否共线 如果共线则不需要处理
            float sin = Vector3.Cross(v1, v2).magnitude;
            if(sin>0)
            {
                //凹点反向
                sin = list[i].isConcave ? -sin : sin;
                //新点顶计算公式:Q = P + (v1+v2);
                Vector3 point = vertices[i] + thickness / sin * (v1 + v2);
                //加入新多边形点顶集合
                vectorList.Add(point);
            }
        }

        //绘制新多边形
        NewPolygon.Vertices = vectorList.ToArray();
        NewPolygon.Draw();
    }

得到新多边形点顶后如何生成三角形网格:
基本思路:选取多边形某个点顶,将该顶点与除相邻顶点外的其他顶点进行连线,将多边形分解成n-2个(n为顶点数)三角形
在这里插入图片描述
选取A点,连线AC与AD
三角形数组:ABC ACD ADE(注意保持所有三角形的点顶顺序是同序,顺时针与逆时针显示的正反面不同)

当遇到特殊的多边形时随机选取顶点进行三角化会出现问题:
在这里插入图片描述
解决方法:当选取某顶点进行三角化时,若按顺序遍历顶点得到的三角形时针顺序不一致则会得到错误的三角化结果,需要选取另一顶点进行三角化。
图中错误情况时:边AB与AC构成三角形ABC是逆时针,边AC与AD构成三角形ACD是顺时针,所以得到了错误的三角化结果

核心代码:

    /// <summary>
    /// 根据点顶顺序将多边形转化为三角形数组
    /// </summary>
    /// <param name="vertex">顶点数组</param>
    /// <returns></returns>
    public int[] Trianglate(Vector3[] vertex)
    {
        if (vertex.Length < 3)
            return null;
        List<int> list = new List<int>();
        int i = 0;
        int j = i + 1;
        int k = i + 2;
        //基准向量(本例选取逆时针的法向量)
        Vector3 stander = new Vector3(0, 0, 1);
        while (true)
        {
            Vector3 tem1 = vertex[j] - vertex[i];
            Vector3 tem2 = vertex[k] - vertex[i];
            //判断是否保持逆时针 
            float tem = Vector3.Dot(Vector3.Cross(tem1, tem2), stander);
            if(tem>0)
            {
                //保持逆时针则将三角形顶点按顺序加入集合中
                list.Add(i);
                list.Add(j);
                list.Add(k);
                j = (++j) % vertex.Length;
                k = (++k) % vertex.Length;
                if (k == i)
                    break;
            }
            else
            {
                //若不保持逆时针则选取下一个顶点进行循环
                i++;
                j = i + 1;
                k = i + 2;
                list.Clear();
                continue;
            }
        }

        return list.ToArray();
    }

效果

在这里插入图片描述

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值