在我的应用程序中(基于Unity3D),网格在运行时加载。在某些网格中,所有面都被翻转了(见图1)。
我尝试实现一个简单的算法,计算所有顶点的中心并检查法线是否指向中心。如果是这种情况,这个面应该翻转。
问题是,(如图2所示)当所有面朝着错误的方向时,该算法只翻转了一些面。
翻转的网格几乎全部都是楼梯(如果有帮助的话)。
如果有人能够指出我的错误或知道更好的方法,我将不胜感激。在Blender中有“重新计算法线”的功能,但我没有理解它的正确性,可能它对于我的问题来说过于复杂。
以下是C#算法和图片:
更新: 感谢Thibault Cimic,通过将IsFacingInwards函数更改为以下内容,代码现在可以正常工作:
我尝试实现一个简单的算法,计算所有顶点的中心并检查法线是否指向中心。如果是这种情况,这个面应该翻转。
问题是,(如图2所示)当所有面朝着错误的方向时,该算法只翻转了一些面。
翻转的网格几乎全部都是楼梯(如果有帮助的话)。
如果有人能够指出我的错误或知道更好的方法,我将不胜感激。在Blender中有“重新计算法线”的功能,但我没有理解它的正确性,可能它对于我的问题来说过于复杂。
以下是C#算法和图片:
public static class FixMeshUtil
{
public static void FixNormals(Mesh mesh)
{
if(mesh.vertexCount != mesh.normals.Length)
mesh.RecalculateNormals();
Vector3[] normals = mesh.normals;
Vector3[] vertices = mesh.vertices;
int[] triangles = mesh.triangles;
Vector3 center = CenterPoint(vertices);
for(int i = 0; i < triangles.Length; i += 3)
{
Vector3 v1 = vertices[triangles[i]];
Vector3 v2 = vertices[triangles[i + 1]];
Vector3 v3 = vertices[triangles[i + 2]];
Vector3 n1 = normals[triangles[i]];
Vector3 n2 = normals[triangles[i + 1]];
Vector3 n3 = normals[triangles[i + 2]];
Vector3 calcNormal = CalculateNormal(v1, v2, v3);
if(!WithinTolerance(n1))
n1 = calcNormal;
if(!WithinTolerance(n2))
n2 = calcNormal;
if(!WithinTolerance(n3))
n3 = calcNormal;
if(IsFacingInwards(calcNormal, center))
Array.Reverse(triangles, i, 3);
}
mesh.normals = normals;
mesh.triangles = triangles;
}
private static Vector3 CenterPoint(Vector3[] vertices)
{
Vector3 center = Vector3.zero;
for(int i = 1; i < vertices.Length; ++i)
center += vertices[i];
return center / vertices.Length;
}
private static bool WithinTolerance(Vector3 normal) => normal.magnitude > 0.001f;
private static bool IsFacingInwards(Vector3 normal, Vector3 center) =>
Vector3.Dot((normal - center).normalized, normal.normalized) < 0f;
private static Vector3 CalculateNormal(Vector3 a, Vector3 b, Vector3 c)
{
Vector3 v1 = b - a;
Vector3 v2 = c - a;
return new Vector3
(
(v1.y * v2.z) - (v1.z * v2.y),
(v1.z * v2.x) - (v1.x * v2.z),
(v1.x * v2.y) - (v1.y * v2.x)
).normalized;
}
}
更新: 感谢Thibault Cimic,通过将IsFacingInwards函数更改为以下内容,代码现在可以正常工作:
Vector3 midPoint = center - ((v1 + v2 + v3) / 3);
//...
if(IsFacingInwards(calcNormal, midPoint))
//...
private static bool IsFacingInwards(Vector3 normal, Vector3 direction) =>
Vector3.Dot(direction.normalized, normal.normalized) > 0f;