从程序生成的管道中去除扭曲

Removing twists from procedurally generated pipe

我在尝试围绕样条线生成网格时遇到问题。

一切都很好,但在样条线上的随机点,网格“扭曲”了。部分三角形发生偏移(cf picture)

https://i.ibb.co/WsFJjdm/Low-Poly-Spline-Bug.png

更详细的网格

https://i.ibb.co/vBDD00V/Detailed-Spline-Bug.png

以及创建网格的代码。我没有在索引 0 处开始填充数组,因为我之前生成了“管”的盖子(如果需要我可以共享代码)

for (int step = 1; step <= _splineDetail; step++)
        {
            Vector3 centerPoint = spline.GetPoint(1f / _splineDetail * step);
            Vector3 centerDir = spline.GetDirection(1f / _splineDetail * step);

            Vector3 perpendicularVect = Vector3.Cross(centerDir, centerPoint).normalized * Radius;
        
            int startVerticesIndex = step * RadiusDetail + 1;
            int endVerticesIndex = startVerticesIndex + RadiusDetail;
            for (int i = startVerticesIndex; i < endVerticesIndex; i++)
            {
                _vertices[i] = Quaternion.AngleAxis(angle * -i, centerDir) * perpendicularVect + centerPoint;
                _normals[i] = _vertices[i] - centerPoint;
            }

            int index = 1 + RadiusDetail * (step - 1);
            int startTrianglesIndex = (step - 1) * RadiusDetail * 6 + RadiusDetail * 3;
            int endTrianglesIndex = startTrianglesIndex + RadiusDetail * 6;
            for (int i = startTrianglesIndex; i < endTrianglesIndex; i += 6)
            {
                _triangles[i] = index + RadiusDetail;
                _triangles[i + 1] = index + 1 + RadiusDetail;
                _triangles[i + 2] = index;

                _triangles[i + 3] = index + 1 + RadiusDetail;
                _triangles[i + 4] = index + 1;
                _triangles[i + 5] = index;
            
                index++;
            }

            _triangles[endTrianglesIndex - 5] = _triangles[startTrianglesIndex];
        
            _triangles[endTrianglesIndex - 3] = _triangles[startTrianglesIndex];
            _triangles[endTrianglesIndex - 2] = _triangles[startTrianglesIndex + 2];
        }

感谢阅读,希望大家帮帮我!

问题是仅仅知道管道的当前位置和方向是不够的。

为了说明,从两个相同的管道开始并排向前,顶部用红色标记,底部用油漆标记为青色。向上弯曲管道 1,然后向右弯曲,然后向下弯曲,然后向右弯曲。向下弯曲管道 2,然后向右弯曲,然后向上弯曲,然后向右弯曲。两根管子的终点和方向相同,但末端的红蓝两边位置不同:

(当然也适用于圆管)

如果您不考虑这种歧义并且始终将“顶部”视为红色,则您必须在管道中有一些点,红色油漆在管道周围“扭曲”。同样的原则是发生在你的网格的边缘和面。


为了解决这个问题,您可以使用四元数的幂来计算当前环上的“第一个顶点”对应于“第一个顶点”在前一个环上应该属于的位置。

这个“第一个顶点”的位置我们称之为“索引方向”。我们可以把这个方向看成是我们“红漆”的方向。实际上,它将是样条中该位置处环的第一个顶点。所以,你的 perpendicularVect 做了同样的事情!

因此,您可以通过找到任何有效的索引方向来开始管道。为了摆脱这些曲折,如何找到它并不重要。具有常数参数的叉积效果很好,一定要处理共线性。

然后,在接下来的每个步骤中,找到将管道的先前方向旋转到新方向的最小旋转。 Quaternion.FromToRotation 正是这样做的。然后,将该旋转应用于索引方向以找到新的索引方向。

无论您在哪一步,一旦有了索引方向,您就可以照原样进行,就好像它是您的 perpendicularVect。围绕管道的轴旋转它以找到环中的其他点,并将相邻点相互连接。然后,继续下一步。


一共:

Vector3 previousCenterDir, indexDir;
for (int step = 1; step <= _splineDetail; step++)
{
    Vector3 centerPoint = spline.GetPoint(1f / _splineDetail * step);
    Vector3 centerDir = spline.GetDirection(1f / _splineDetail * step);
    if (step == 1)
    {
        indexDir = Vector3.Cross(centerDir, Vector3.forward);
        if (indexDir == Vector3.zero) 
        {
            // handle case where pipe direction is colinear with forward
            indexDir = Vector3.Cross(centerDir, Vector3.right);
        }
        indexDir = indexDir * Radius;
    }
    else 
    {
        indexDir = Quaternion.FromToRotation(previousCenterDir, 
                centerDir) * indexDir;
    }
    previousCenterDir = centerDir;

    int startVerticesIndex = step * RadiusDetail + 1;
    int endVerticesIndex = startVerticesIndex + RadiusDetail;
    for (int i = startVerticesIndex; i < endVerticesIndex; i++)
    {
        _vertices[i] = Quaternion.AngleAxis(angle * -i, centerDir) * indexDir 
                + centerPoint;
        _normals[i] = _vertices[i] - centerPoint;
    }
    int index = 1 + RadiusDetail * (step - 1);
    // ... rest of code from question ...
}