细分和三角化多边形

Subdividing and triangulating a polygon

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

一般情况下,我会通过标准的剪耳算法来实现,结果会是这样的 (B)

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

这里有一个相关的 SO 线程更深入地讨论了这个问题

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

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

但我坚持不懈地去做。不知何故,我不知道应该如何生成三角数据 (A -> C)。有人可以用这种方式帮助我 splitting/triangulating 网格算法或提供想法吗?假设所有的“方块”都是N×N的方块,N是我指定的。

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

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

根据需要细分:

所以,我成功了。

想法大纲:

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

我还确保跟踪独特的矢量(因为我最终在分界线上得到了双倍的顶点)。这也有助于我以后更轻松地创建顶点数组。

这里有几个链接:

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

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);
    }