网格颜色混乱可能是由于顶点法线计算错误

Posted

技术标签:

【中文标题】网格颜色混乱可能是由于顶点法线计算错误【英文标题】:Mesh color is messed up probably due to bad vertex normal computation 【发布时间】:2020-10-14 07:26:17 【问题描述】:

我有顶点 positionindex 我想要顶点 normal:

// input
vector<Vec3f> points = ... // position
vector<Vec3i> facets = ... // index (triangles)

// output
vector<Vec3f> norms; // normal

方法一

我这样计算正常:

    norms.resize(points.size()); // for each vertex there is a normal

    for (Vec3i f : facets) 
        int i0 = f.x();
        int i1 = f.y(); // index
        int i2 = f.z();
        Vec3d pos0 = points.at(i0);
        Vec3d pos1 = points.at(i1); // position
        Vec3d pos2 = points.at(i2);

        Vec3d N = triangleNormal(pos0, pos1, pos2); // face/triangle normal

        norms[i0] = N;
        norms[i1] = N; // Use the same normal for all 3 vertices
        norms[i2] = N;
    

然后,输出网格用 Phong 材质渲染如下:

反转法线的方法1

当我在方法1中反转法线方向时:

        norms[i0] = -N;
        norms[i1] = -N;
        norms[i2] = -N;

暗区和亮区互换:

通过以下方式将位置 0 与位置 1 交换也会发生同样的情况:

        // Vec3d N = triangleNormal(pos0, pos1, pos2);
        Vec3d N = triangleNormal(pos1, pos0, pos2); // Swap pos0 with pos1

方法二

我通过this method计算法线:

    // Count how many faces/triangles a vertex is shared by
    vector<int> counters;
    counters.resize(points.size());

    norms.resize(points.size());
    for (Vec3i f : facets) 
        int i0 = f.x();
        int i1 = f.y(); // index
        int i2 = f.z();
        Vec3d pos0 = points.at(i0);
        Vec3d pos1 = points.at(i1); // position
        Vec3d pos2 = points.at(i2);
        
        Vec3d N = triangleNormal(pos0, pos1, pos2);

        // Must be normalized
        // https://***.com/a/21930058/3405291
        N.normalize();

        norms[i0] += N;
        norms[i1] += N; // add normal to all vertices used in face
        norms[i2] += N;

        counters[i0]++;
        counters[i1]++; // increment count for all vertices used in face
        counters[i2]++;

    

    // https://***.com/a/21930058/3405291
    for (int i = 0; i < static_cast<int>(norms.size()); ++i) 
        if (counters[i] > 0)
            norms[i] /= counters[i];
        else
            norms[i].normalize();
    

此方法通过 Phong 材质生成完全黑暗的最终渲染:

我还尝试了建议的 here 和 there 方法,它们与方法 2 类似。它们都会产生类似于方法 2 的最终渲染,即所有黑暗区域都没有任何光线。

反转法线的方法2

我使用了方法2,但最后我将正常方向颠倒了:

    for (Vec3d & n : norms) 
        n = -n;
    

令我惊讶的是,最终的渲染都是黑暗的:

同样在方法 2 中,我尝试将位置 0 与位置 1 交换:

        // Vec3d N = triangleNormal(pos0, pos1, pos2);
        Vec3d N = triangleNormal(pos1, pos0, pos2); // swap pos0 with pos1

最终的渲染是所有黑暗区域,没有任何明亮的区域。

如何?

知道如何让我的最终渲染变得全亮而没有任何暗区吗?

【问题讨论】:

顺便说一句,您的triangleNormal 是否正常工作(假设它使用叉积来获得法线),或者您是否将 3D 位置传递给它们?因为您的圆柱体沿圆周具有相同的颜色,这表明它们可能不是 3D 或正确的 @Spektre Vec3d MyClass::triangleNormal(Vec3d pos0, Vec3d pos1, Vec3d pos2) Vec3d V = pos1 - pos0; Vec3d W = pos2 - pos0; Vec3d N = V.cross(W); return N; 看起来不错,但我会添加归一化,因此法线是单位向量......现在返回的向量的大小等于三角形面积的两倍,如果不处理也可能会弄乱你的照明计算 【参考方案1】:

看起来您的网格没有一致的缠绕规则。因此,一些三角形/面被定义为 CW 其他顶点的 CCW 顺序,导致您的一些法线朝向相反的方向。您可以采取一些措施来补救:

    使用双面法线照明

    这是最简单的...片段中的某处或您正在计算阴影的任何地方,如下所示:

    out_color = face_color*(ambient_light+diffuse_light*max(0.0,dot(face_normal,light_direction)));
    

    当法线方向错误时,dot 的结果为负数,导致颜色变深,因此只需使用 abs 值代替:

    out_color = face_color*(ambient_light+diffuse_light*abs(dot(face_normal,light_direction)));
    

    在固定功能管道中,甚至还有这个 IIRC 的开关:

    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
    

    修复网状缠绕

    必须有 3D 工具来执行此操作(Blender、3DS、...),或者如果您的网格是动态生成的,您可以更新您的代码以自行创建一致的绕组。

    正确的缠绕使您能够使用GL_CULL_FACE,从而大大加快了渲染速度。它还支持更高级的东西,例如:

    OpenGL - How to create Order Independent transparency?

    修复法线

    在某些情况下,有一些方法可以检测法线是向外还是向内指向网格,例如:

    Determing the direction of face normals consistently?

    所以只要在计算正常的过程中否定错误的,就是这样。但是,如果您的网格太复杂(离凸面太远),这不是那么容易完成的,因为您需要使用网格的局部“中心”,甚至需要使用昂贵的多边形内部测试。

生成法线的平均方法会为法线的两个方向提供深色,这意味着您错误地计算了它们并且它们很可能为零。有关此类方法的更多信息,请参阅:

How to achieve smooth tangent space normals?

无论如何要调试这样的问题,最好将法线渲染为从网格顶点开始的线(使用线框)。然后你会直接看到法线的好坏。这里的例子:

【讨论】:

我的问题还没有解决,但你的帖子很有道理=)

以上是关于网格颜色混乱可能是由于顶点法线计算错误的主要内容,如果未能解决你的问题,请参考以下文章

[Unity Shader] 逐顶点光照和逐片元光照

OpenGL,交错的 VBO(顶点、法线和颜色)与光

Maya2018多变形建模过程中顶点法线方向不一致问题的解决方法

Ogre的Ocean例子理解

如何在OpenGL中计算三角形网格的顶点法线?

如何在OpenGl中计算三角形网格的顶点法线?