基于法线和中心位置固定网格中翻转面的算法

问题描述

在我的应用程序(基于Unity3D)中,网格在运行时加载。在某些网格中,所有面都被翻转(见图1)。 我试图实现一种简单的算法,该算法计算所有顶点的中心并检查法线是否指向该中心。如果是这种情况,则应翻转这张脸。 问题在于(如您在图片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;

Flipped mesh

"Fixed" Mesh

解决方法

我不确定您是否给IsFacingInwards()提供了正确的数学对象;

放置IsFacingInwards(calcNormal,center-p),其中p是组成要从其向外求法线的边的顶点之一?

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...