【问题标题】:Algorithm to fix flipped faces in a mesh based on the normals and center position基于法线和中心位置修复网格中翻转面的算法
【发布时间】:2020-08-13 12:43:28
【问题描述】:

在我的应用程序(基于 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

【问题讨论】:

    标签: algorithm unity3d 3d mesh normals


    【解决方案1】:

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

    Put IsFacingInwards(calcNormal,center-p) 其中 p 是构成您希望向外法线的边的顶点之一?

    【讨论】:

    • 我也试过了(只使用第一个法线)。但结果是一样的。
    • center 是内圈的中心吗?是二维有限元吗?
    • center 是所有顶点的中心(基本上是“内点”)。指向那个“内点”的每个法线都朝内。这就是为什么我要计算(正常 - 中心)以将方向指向“内点”。如果方向与法线匹配,则其指向内部。我希望这是可以理解和正确的。
    • 是的,但是 normal 是一个向量,而 center 是一个点(坐标的平均值),所以你应该取 center-p ,它是从 p 到中心的向量。 center-p 向内进入元素,例如你想计算向外法线的女巫的边缘顶点之一
    • 没问题 ;) 如果真的是 2D 有限元编码,我通常做的是在搜索边界边时,我还存储第三个顶点的索引,使其成为三角形并稍后使用确保计算向外的法线
    猜你喜欢
    • 2020-05-08
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多