如何一致确定面法线的方向?

4

我是一个计算机图形学的新手,如果我的语言不准确或者问题基础的话请多多包涵。

在给定顶点列表和面列表的情况下,能否正确计算出面法线呢:

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都指定了三维空间中的顶点位置(但不一定是向量)。
每个f_i包含指定它的三个顶点的索引。
我知道可以使用面的两边的叉积来得到法线,但该法线的方向取决于两边的顺序和选择(据我所知)。
考虑到这是我唯一拥有的数据,是否可能正确确定法线的方向?或者至少能够一致地确定它们吗?(所有法线可能都指向错误的方向?)
2个回答

4
在计算机图形学中,多边形的面法线通常通过多边形的定向规则来确定。这意味着,当从面对多边形的方向观察时,所有的面都定义了点按照顺时针(CW)或逆时针(CCW)顺序排列。然后使用叉积就能保证法线的一致性。
然而,很多网格并不符合这个定向规则(有些面是顺时针的,其他是逆时针的),这就成了一个问题。我知道两种解决方法:
  1. for simple shapes (not too much concave)

    the sign of dot product of your face_normal and face_center-cube_center will tell you if the normal points inside or outside of the object.

    dot

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

    You can even use any point of face instead of the face center too. Anyway for more complex concave shapes this will not work correctly.

  2. test if point above face is inside or not

    simply displace center of face by some small distance (not too big) in normal direction and then test if the point is inside polygonal mesh or not:

    displacement

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

    to check if point is inside or not you can use hit test.

然而,如果法线只用于光照计算,则通常在点积中使用。因此,我们可以使用其绝对值,这将解决所有光照问题,而不管法线的方向。例如:

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

一些图形API已经实现了这一功能(查找双面材料或法线,通常打开它们会使用绝对值...)。例如,在OpenGL中:
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

4

通常情况下,没有办法在一组三维面上“一致地”分配正常值……以莫比乌斯带为例。

Möbius strip image

你会注意到,如果您在一圈后开始行走,您将到达同一点,但位于相反的一侧。换句话说,这条带子没有两个面,只有一个面。如果您用三角形带构建这样的形状,当然就无法以一致的方式分配法线,您必须最终拥有两个相邻的三角形,其法线指向相反的方向。
话虽如此,如果您的三角形集确实是可定向的(即确实存在一致的法线分配),解决方案是从一个三角形开始,然后像洪水填充算法一样传播到邻居。例如,在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

谢谢你的回答,如果可能的话,我也会认可这个答案! - Abe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接