从程序生成的管道中去除扭曲
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 ...
}
我在尝试围绕样条线生成网格时遇到问题。
一切都很好,但在样条线上的随机点,网格“扭曲”了。部分三角形发生偏移(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 ...
}