如何通过平均法线算法平滑网格表面?

时间:2018-07-04 02:21:04

标签: c++ vector vertex

在很多3D设计程序中,例如Blender和3D Studio Max,您可以选择要平滑的形状的表面,而不是硬和平的。这等于对与特定顶点共享相同位置的顶点法线进行平均(我假设)。我正在尝试编写某种形式的东西,但这是我第一次这样做,我想出的方法必须是效率很低的,我相信这是n ^ 2的时间复杂性。因此,假设我们有一个具有99个顶点的3D形状,则循环必须运行99 x 99次,几乎是10,000次。

使用球很容易,因为每个顶点的法线就是顶点的标准化位置(该位置的单位矢量)。我可以做到这一点,如下所示:

enter image description here

我想知道这些程序是如何做到的,以及是否有比这更有效的方法。我的方法是从0到99循环遍历i,从0到99遍历j。我基本上检查了另一个顶点是否具有相同的位置,如果是,则检查其法线与另一个顶点的法线之间的角度在所需的角度阈值以下。如果是这样,我将其添加到向量的总和中,然后除以匹配的顶点数。我不确定这是否正确。

#include <iostream>
#include <vector>
#include <math.h>

using namespace std;

float thresholdAngle = 60 /*degrees*/ / 180 * 3.14159; // In radians

struct vec3
{
    float x, y, z;
    bool hasSamePositionAs(vec3& other)
    {
        if (other.x == this->x &&
            other.y == this->y &&
            other.z == this->z)
            return true;
        return false;
    }

    bool angleIsUnderThresholdWith(vec3& other)
    {//THIS SHOULD BE IF DOTPRODUCT > COS(THRESHOLDANGLE)
        return dotProductWith(other) < thresholdAngle ? true : false;
    }

    float dotProductWith(vec3& other)
    {
        return x * other.x + y * other.y + z * other.z;
    }

    void operator +=(vec3 other) { x += other.x; y += other.y; z + other.z; }

int main() 
{
    // 33 triangles, 99 vertices
    vector<vec3> vertices(99); // ALL UNIT VECTORS, NORMALS
    vector<vec3> newNormals(99);

    for (int i = 0; i < 99; i++)
    {
        vec3 normal = vertices[i];

        for (int j = 0; j < 99; j++)
        {
            if (j == i) continue;
            if (vertices[i].hasSamePositionAs(vertices[j]))
            {
                if (vertices[i].angleIsUnderThresholdWith(vertices[j]))
                {
                    normal += vertices[j];
                }
            }
        }

        normalise(normal);          
        newNormals[i] = normal;
        }
    }

编辑:好的,所以使用这段代码我设法得到了我想要的,但是算法仍然是n ^ 2。您会注意到,作为法线的蓝线在平面阴影中针对每个面分开,并在通过此代码时合并在一起(取平均值)。

enter image description here

0 个答案:

没有答案