如何检测反转面(翻转法线)?

How to detect reversed faces (flipped normals)?

我需要在一个场景中加载多个 STL 文件,其中一些文件有 reversed/flipped 个面孔。这可以使用双面 material 轻松修复(视觉上),但我尝试的是可视化错误的面孔,将 material 索引从蓝色切换为红色。

据我所知,mirrored/flipped/reversed 面是顺时针绘制的,而不是逆时针绘制的。但我们是在谈论构成面部的三个向量吗?如果是的话,绕线顺序就是face.a、face.b和face.c处​​设置的顺序?还是我必须比较面部索引值才能知道绘制面部的顺序?

我设置了一个JSFiddle来说明这个问题:这个STL模型有10个面,其中3个是颠倒或翻转的。使用 FaceNormalsHelper,您可以看到这 3 个法线助手指向模型的内部,而不是外部。我尝试了 6 种不同的方法来检查这一点,其中一些是从 computeFaceNormals() 的代码中提取的,但没有成功。我还检查了 FaceNormalsHelper 以了解它是如何工作的,但它只是在法线方向上从面质心画一条线,但它永远不会计算它是指向内部还是外部。

(注意:为了从一种方法变为另一种方法,必须在脚本开始时更改变量。方法1手动覆盖这三个面material索引,只是可视化错误的(作为所有其他方法的验证)。没有其他人得到好的结果。但是方法 2 和 3 真的非常接近它,尽管不知何故检测到另外两个人脸被翻转了。我想知道是否面部角度与此有关)

编辑: @manthrax 建议的方法非常有效,将每个面的边缘与所有其他面进行比较。如果边以相同方式绘制(例如:面 1 边 a-b 与面 2 边 a-b 匹配,而不是 b-a),则它会翻转。基本上,如果一张脸有两条或多条标记为翻转的边,我们可以说整张脸都翻转了。检查 JSFiddle 以查看它是否正常工作。

function paintFlippedFaces(faceCheck){
    for(var x=0; x<Object.keys(faceCheck).length; x++){
    var flipCounter = 0;
    if(faceCheck[x]['a-b']) flipCounter++;
    if(faceCheck[x]['b-c']) flipCounter++;
    if(faceCheck[x]['c-a']) flipCounter++;        
    console.log("face " + x + " has " +flipCounter+ " flipped edges" );
    if(flipCounter >= 2){
      geometry.faces[x].materialIndex = 1;
      //geometry.faces.elementsNeedUpdate = true;
    }
  } 
}

首先,"flipped" 面孔完全是主观的。您需要决定一些参数。 对于凸物体,有一个非主观的定义。例如,点缀有一个面顶点的面法线 > 0.

对于非凸物体,您可以根据顶点的缠绕顺序以及是否有任何 2 条边包含相同的顺序进行一些猜测,这意味着您有 1 个正面三角形和 1 个背面三角形共享一条边。 (虽然在非凸的情况下不一定可以识别这些三角形中的哪些三角形。)

首先,您必须对几何体进行 .mergeVertices,以便顶点在各个面上共享...我认为 stl 为每个三角形定义了唯一的顶点,因此没有 "edge" 的概念由两张脸共享。

然后你可以遍历每个面..基本上共享相同 2 个顶点的任何 2 条边应该有相反的顺序,或者面彼此相对..

因此,如果 2 个面有像 3,2 和 2,3 这样的边,那么它们都面向相同的方向。并且面是一致的。

如果两个不同的面都有 3,2 和 3,2 的边,则它们面向相反的方向,您可以将两个面都标记为可能被翻转。被标记次数最多的面孔可能实际上是翻转的面孔。

这些是我能想到的用于确定 inside/outside 的最佳猜测测试,但这是一个棘手的问题。

Blender 有几个 "recalculate normals" 方法来重新计算 "inside"/"outside",我认为它们的运行原理相似。所以你也可以看看那些。