判断人脸法线方向一致?

Determing the direction of face normals consistently?

我是计算机图形学的新手,所以如果我的某些语言不准确或问题遗漏了一些基本知识,我深表歉意。

给定一个顶点列表和一个像这样的面列表,是否可以正确计算面法线:

v1: x_1, y_1, z_1
v2: x_2, y_2, z_2
...
v_n: x_n, y_n, z_n
f1: v1,v2,v3
f2: v4,v2,v5
...
f_m: v_j, v_k, v_l

每个 x_i, y_i , z_i 指定 3d 中的顶点位置 space(但不一定是矢量)

每个 f_i 包含指定它的三个顶点的索引。

我知道你可以使用面的两条边的叉积来得到法线,但是法线的方向取决于边的顺序和选择(据我了解)。

鉴于这是我仅有的数据,是否可以正确确定法线的方向?或者是否有可能至少始终如一地确定它们? (所有法线可能都指向错误的方向?)

在CG中,它是通过多边形缠绕规则完成的。这意味着所有的面都被定义了,所以当直接在面上看时,点是按 CW(或 CCW)顺序排列的。然后使用叉积将导致法线一致。

然而,许多网格不符合缠绕规则(有些面是顺时针,有些是逆时针,但不完全相同),这就是一个问题。我知道有两种方法:

  1. 形状简单(不要太凹)

    你的 face_normalface_center-cube_center 的点积符号会告诉你法线是指向对象的内部还是外部。

    if ( dot( face_normal , face_center-cube_center ) >= 0.0 ) normal_points_out
    

    您甚至可以使用面部的任意点来代替面部中心。无论如何,对于更复杂的凹形,这将无法正常工作。

  2. 测试面上方的点是否在里面

    简单地将面的中心沿法线方向移动一小段距离(不要太大),然后测试该点是否在多边形网格内:

    if ( !inside( face_center+0.001*face_normal ) ) normal_points_out
    

    检查点是否在里面你可以使用hit test

然而,如果法线仅用于光照计算,那么它的用法通常在点积内。所以我们可以使用它的 abs 值来代替,这将解决所有的照明问题,无论法线侧如何。例如:

output_color = face_color * abs(dot(face_normal,light_direction))

一些 gfx api 已经实现了这一点(寻找双面材料或法线,打开它们通常使用 abs 值...)例如在 OpenGL 中:

glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

一般来说,无法在一组 3d 面上分配法线 "consistently"...以著名的莫比乌斯带为例...

您会注意到,如果您在一个循环后开始在上面行走,您会到达同一点但在另一侧。换句话说,这个条带没有两个面,而只有一个。如果你用三角形带构建这样的形状,当然没有办法以一致的方式分配法线,你最终必然会有两个相邻的三角形,法线指向相反的方向。

就是说,如果您的三角形集合确实是可定向的(即实际上存在一致的法线分配),解决方案是从一个三角形开始,然后像在洪水填充算法中一样传播到邻居。例如在 Python 中它看起来像:

active = [triangles[0]]
oriented = set([triangles[0]])
while active:
    next_active = []
    for tri in active:
        for other in neighbors(tri):
            if other not in oriented:
                if not agree(tri, other):
                    flip(other)
                oriented.add(other)
                next_active.append(other)
    active = next_active