细分和三角剖分多边形

Posted

技术标签:

【中文标题】细分和三角剖分多边形【英文标题】:Subdividing and triangulating a polygon 【发布时间】:2020-06-23 04:45:32 【问题描述】:

我得到一个随机轮廓作为输入。我需要将其转换为多边形并将其显示在 3d 空间中。 (A)

通常,我会通过标准的剪耳算法来完成,结果会类似于 (B)

但是,由于我正在使用的显卡 (VIVANTE GC 2000) 的图形驱动程序中存在错误,我只能对这样的小形状进行三角测量。原因是,在渲染时,如果网格的顶点位于截锥体的左侧或右侧太远 - 位置计算不正确。这会导致屏幕上大网格的剧烈闪烁和变形。 这是一个已确认的驱动程序问题,并且不会在其他平台上发生,甚至对于同一显卡的旧版本驱动程序也不会发生。不幸的是,我不能使用旧的驱动程序,而且卡制造商不太可能修复这个错误,因为它已经知道了将近十年。

这是一个相关的 SO 线程,更深入地探讨了问题OpenGL Perspective Projection Clipping Polygon with Vertex Outside Frustum = Wrong texture mapping?

所以我必须使用拐杖。换句话说 - 我必须将我的网格分成几个较小的三角形,例如 (C) 这样在任何时间点,渲染的三角形的顶点都不会超出平截头体太远。

不幸的是,我看到真的没有办法做到这一点。我知道这是一个非常不雅的解决方案,但实际上没有其他方法可以解决驱动程序错误。

但我一直坚持做这件事。不知何故,我不知道应该如何生成三角数据(A -> C)。有人可以帮助我以这种方式分割/三角剖分网格的算法或给出想法吗?假设所有“正方形”都是 N×N 正方形,其中 N 由我指定。

或者也许有人对我如何处理这个问题有其他建议。

【问题讨论】:

【参考方案1】:

我猜你可以考虑在拥有 B 后继续细分每个三角形,如下所示:

尽可能多的细分:

【讨论】:

我确实考虑过这一点。但这会导致比我提供的 C 示例更多的三角形,考虑到它是一个 ARM 设备,我宁愿拥有更少而不是更多。不过,谢谢你的提示!任何这样的想法都是有价值的,因为我可能没有想到这一点。 =) 这种其他类型的细分(我已经编辑了我的回复)允许您仅细分那些超出平截头体的三角形,而无需细分那些正确的三角形。最后你会有更少的三角形 我不认为在用户旋转相机时根据网格的哪些部分落在截锥体之外不断改变网格组合是有效的...... 我并没有考虑动态地做它,而是考虑三角形边长的一种阈值并相应地细分。无论如何,我不知道细节。这只是一个想法。我猜 C 解决方案并不像看起来那么简单。 不是,这就是我创建该主题的原因 =D。无论如何,谢谢你的想法,我很感激。【参考方案2】:

所以,我成功了。

构思大纲:

画轮廓 为它计算 X 和 Y 的二等分线(可能是多条线 将初始轮廓细分为“切片”,在某些 Y 坐标上将其一分为二 遍历每个切片并在某些 X 坐标上分割其边缘。 对生成的每个子网格进行三角剖分。

我还确保跟踪唯一向量(因为我最终会在分割线上的顶点得到双精度数)。这也有助于我以后更轻松地创建顶点数组。

这里有几个链接:

细分非凸轮廓:https://geidav.wordpress.com/2015/03/21/splitting-an-arbitrary-polygon-by-a-line/ 耳夹三角测量:https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf

我的代码(有点乱,我打算重构它,但另一方面它是一体的)

public TriangulationOutput triangulateSubdivide(List<Vector2f> contour)
    
        // clear lists and reset variables
        input.clear();
        polygonVerts.clear();;
        convexVerts.clear();
        reflexVerts.clear();
        straightVerts.clear();
        earVerts.clear();
        canBeEars.clear();
        corner = null;
        uniqueVectors.clear();

        List<Triangle>  result = new ArrayList<>();

        // Reverse the order of verts if the list is clockwise
        if (isClockwise(contour))
        
            Collections.reverse(contour);
        

        // find leftmost and topmost points in the
        Vector2f top = contour.get(0);
        Vector2f left = contour.get(0);
        Vector2f bottom = contour.get(0);
        Vector2f right = contour.get(0);
        for (int i = 1; i < contour.size(); i++)
        
            Vector2f current = contour.get(i);
            if (current.y > top.y)
                top = current;
            if (current.y < bottom.y)
                bottom = current;
            if (current.x < left.x)
                left = current;
            if (current.x > right.x)
                right = current;
        

        // check if the entire mesh fits within the space
        if ((Math.abs(top.y - bottom.y) <= GlobalSettings.OPT_MAX_DISTANCE)&&(Math.abs(right.x - left.x) <= GlobalSettings.OPT_MAX_DISTANCE))
        
            // I haven't tested this edge case yet, but it's not really relevant to the algorythm
            System.err.println("TriangulateSubdivide -> shortcut used");
            return new TriangulationOutput(triangulateSimple(contour), contour);
            //TODO: Could be trouble
        

        //Find X and Y split coordinates
        List<Float> linesY = new ArrayList<>();
        float lineCoord = ((float)((int)(top.y / GlobalSettings.OPT_MAX_DISTANCE))) * GlobalSettings.OPT_MAX_DISTANCE;
        while (lineCoord > bottom.y)
        
            linesY.add(lineCoord);
            lineCoord -= GlobalSettings.OPT_MAX_DISTANCE;
        

        List<Float> linesX = new ArrayList<>();
        lineCoord = ((float)((int)(right.x / GlobalSettings.OPT_MAX_DISTANCE))) * GlobalSettings.OPT_MAX_DISTANCE;
        while (lineCoord > left.x)
        
            linesX.add(lineCoord);
            lineCoord -= GlobalSettings.OPT_MAX_DISTANCE;
        

        List<List<Vector2f>> submeshes = new ArrayList<>();
        List<Vector2f> contourCpy = new ArrayList<>();
        contourCpy.addAll(contour);
        
        for (int i = 0; i < linesY.size(); i++)
        
            List<Vector2f> submesh;
            List<Vector2f> intersections = new ArrayList<>();

            float yCoord = linesY.get(i);

            // split polygon edges on dividing horizontal lines
            // store found intersections to find them easier later
            for (int j = 0; j < contourCpy.size(); j++)
            
                Vector2f current = contourCpy.get(j);
                int index = (j - 1) < 0 ? contourCpy.size()-1 : (j - 1);
                Vector2f previous = contourCpy.get(index);
                index = (j + 1) >= contourCpy.size() ? 0 : (j + 1);
                Vector2f next = contourCpy.get(index);

                // determines on which side of the line vertexes lie, or if they lie directly on it
                VertexStatus vsCurrent = new VertexStatus(current, yCoord, true);
                VertexStatus vsNext = new VertexStatus(next, yCoord, true);
                VertexStatus vsPrevious = new VertexStatus(previous, yCoord, true);

                if (vsPrevious.isOn() && vsCurrent.isOn() && vsNext.isOn())
                
                    // adjacient edges lie completely on the line
                    continue;
                

                if (vsCurrent.isOn())
                
                    // add point if it lies on the line
                    intersections.add(current);
                
                else if ((vsCurrent.status() != vsNext.status()) && (!vsNext.isOn()))
                
                    // line intersects current edge in a point other than vertexes
                    float x = current.x + ((yCoord - current.y)*(next.x - current.x)) / (next.y - current.y);
                    Vector2f ip = new Vector2f(x, yCoord);
                    intersections.add(ip);
                    contourCpy.add(index, ip);       //TODO: possible trouble at last node
                    j++;    //increment to skip the point we just added
                
            

            //sort intersections
            intersections.sort(new Comparator<Vector2f>()
            
                @Override
                public int compare(Vector2f v1, Vector2f v2)
                
                    return (v1.x < v2.x) ? -1 : 1;
                
            );

            // find submeshes that lie above the line. Every two intersections 
            for (int j = 0; j < intersections.size(); j+=2)
            
                // for every two points we find a linked submesh
                submesh = new ArrayList<>();

                int indexEnd = contourCpy.indexOf(intersections.get(j));
                int indexStart = contourCpy.indexOf(intersections.get(j+1));

                int index = indexStart;
                boolean cont = true;
                while (contourCpy.size() > 0)
                

                    submesh.add(contourCpy.get(index));

                    if (index == indexEnd)
                    
                        break;
                    

                    if ((index != indexStart))
                    
                        // remove points between intersections from future countour
                        contourCpy.remove(index);

                        if (index < indexEnd)
                        
                            indexEnd--;
                        
                    
                    else
                    
                        index++;
                    

                    if (index >= contourCpy.size())
                    
                        index = 0;
                    
                
                //while (index != indexEnd);

                submeshes.add(submesh);
            
        

        // add the remaining contour as final bottom-most mesh
        submeshes.add(contourCpy);
        
        for (List<Vector2f> submesh : submeshes)
        
            // Add more vertexes for X coord divisions
            for (int i = 0; i < submesh.size(); i++)
            
                Vector2f current = submesh.get(i);

                // add current vector to unique
                boolean add = true;
                for (int v = 0; v < uniqueVectors.size(); v++)
                
                    if (uniqueVectors.get(v).equals(current))
                    
                        add = false;
                        break;
                    
                
                if (add)
                
                    uniqueVectors.add(current);
                

                int index = (i + 1) >= submesh.size() ? 0 : (i + 1);
                Vector2f next = submesh.get(index);

                for (int j = 0; j < linesX.size(); j++)
                
                    float xCoord = linesX.get(j);
                    VertexStatus vsCurrent = new VertexStatus(current, xCoord, false);
                    VertexStatus vsNext = new VertexStatus(next, xCoord, false);

                    if (vsCurrent.isOn() || vsNext.isOn())
                    
                        continue;
                    

                    if (vsCurrent.status() != vsNext.status())
                    
                        // vectors lie on different sides of xCoord
                        float y = current.y + ((next.y - current.y)*(xCoord - current.x)) / (next.x - current.x);
                        Vector2f ip = new Vector2f(xCoord, y);
                        // add current vector to unique
                        add = true;
                        for (int v = 0; v < uniqueVectors.size(); v++)
                        
                            if (uniqueVectors.get(v).equals(ip))
                            
                                ip = uniqueVectors.get(v);
                                add = false;
                                break;
                            
                        
                        if (add)
                        
                            uniqueVectors.add(ip);
                        
                        submesh.add(index,ip);

                        //TODO: possible trouble here
                        if (current.x > next.x)
                        
                            index++;
                        
                        i++;
                    
                
            

            result.addAll(triangulateSimple(submesh));
        

        // this basically just stores triangles and a list of vertexes and doesn't do anything else.
        return new TriangulationOutput(result, uniqueVectors);
    

【讨论】:

以上是关于细分和三角剖分多边形的主要内容,如果未能解决你的问题,请参考以下文章

用非凸多边形裁剪和三角剖分三角形

带洞多边形三角剖分

1039. 多边形三角剖分的最低得分

数学篇08 # 如何利用三角剖分和向量操作描述并处理多边形?

Three.js 多边形三角剖分在伪重复点中失败

从 Delaunay 三角剖分计算 alpha 形状的边界多边形