100%平滑度的平滑顶点法线计算的一般方法。

问题描述 投票:5回答:2

我在网上找了好久,都没有找到答案.为了简单起见,我们就假设我需要完全平滑一组共面的法线。我想找到一组向量之间的实际几何双曲,忽略重复的法线,并保持三角汤的精度。. 基本上,它需要。

  • 不管是3个、4个还是4000个三角形,法线仍然需要以几何正确的方式一起工作,而不是偏向于任意的区域
  • 忽略重叠(平行)的法线--如果我有一个立方体的边聚成3个三角形,或者是两条边各聚成一个三角形,最后一条边聚成2个(或更多),或者是只有一条边上有一百万个三角形的立方体,那么双轴一定不能改变

我似乎找到的最常见的法线平滑公式是,将它们简单的平均,将法线向量相加,然后除以三;例如。

normalize((A + B + C) / 3);

当然除以三是没有用的 这已经代表了人们提出的天真无邪的粗暴武力平均法的氛围;这种方法的问题还在于它对均匀的三角汤和平行法线会造成很大的混乱。

我似乎发现的另一个说法是保留初始 "面 "法线,因为它们来自于生成它们的普通交叉相乘操作,因为这样它们就会对三角形的面积进行(有点)加权。在某些情况下,这可能是你想做的事情,然而我需要的是纯双曲线,所以面积一定不会影响公式,即使考虑到它,它仍然会被三角汤搞乱。

我看到有一个提到的方法,说要对相邻面之间的角度进行加权,但我似乎无法正确地实现这个公式--要么就是没有做到我想要的。然而这一点我也不好说,因为我找不到一个简明的解释,而且我的脑子也因为这些废寝忘食的脑力激荡而变得麻木。

有谁知道一个通用的公式吗?如果有帮助的话,我正在用C++和DirectX11工作。


编辑: 下面是一些类似的问题,介绍一些方法。

还有这篇文章。http:/www.bytehazard.comarticlesvertnorm.html

遗憾的是,我试过的实现方法都不行,我也找不到一个清晰、简洁的说法,到底哪个公式才是我需要的。经过一番试错,我终于弄明白了按角度加权是正确的方法,只是我没能正确地实现,由于现在似乎可以用了,我把我的实现方法作为答案加在下面。

algorithm 3d normals vertices
2个回答
5
投票

正确的方法是将 "面 "的法线加权后与 相邻两顶点间的夹角 的一个共享的边缘角。

(这里显示的是相对于每个面的角度)per face angle for common point

这里是一个实例实现的概要。

for (int f = 0; f < tricount; f++)
{
    // ...
    // p1, p2 and p3 are the points in the face (f)

    // calculate facet normal of the triangle using cross product;
    // both components are "normalized" against a common point chosen as the base
    float3 n = (p2 - p1).Cross(p3 - p1);    // p1 is the 'base' here

    // get the angle between the two other points for each point;
    // the starting point will be the 'base' and the two adjacent points will be normalized against it
    a1 = (p2 - p1).Angle(p3 - p1);    // p1 is the 'base' here
    a2 = (p3 - p2).Angle(p1 - p2);    // p2 is the 'base' here
    a3 = (p1 - p3).Angle(p2 - p3);    // p3 is the 'base' here

    // normalize the initial facet normals if you want to ignore surface area
    if (!area_weighting)
    {
        normalize(n);
    }

    // store the weighted normal in an structured array
    v1.wnormals.push_back(n * a1);
    v2.wnormals.push_back(n * a2);
    v3.wnormals.push_back(n * a3);
}
for (int v = 0; v < vertcount; v++)
{
    float3 N;

    // run through the normals in each vertex's array and interpolate them
    // vertex(v) here fetches the data of the vertex at index 'v'
    for (int n = 0; n < vertex(v).wnormals.size(); v++)
    {
        N += vertex(v).wnormals.at(n);
    }

    // normalize the final normal
    normalize(N);
}

这是一个 "天真的 "法线平均的例子(即没有角度加权)。

enter image description here

你可以看到面的分量都是一样的,但由于一些面有两个面,它们的部分插值增加了一倍,使得平均值偏斜。只对表面积进行加权,而不对角度进行加权,会产生类似的结果。

这是相同的模型,但启用了角度加权。

enter image description here

现在插值的法线在几何上都是正确的。

© www.soinside.com 2019 - 2024. All rights reserved.