opengl计算面部法线

goh*_*goh 3 c++ opengl

我正在尝试将从 Blender 导出的模型加载到 OpenGL 中。特别是,我按照本教程中的源代码来帮助我入门。因为加载器相当简单,所以它只读取顶点坐标和面索引,忽略法线和纹理。

然后计算每个面的法线:

        float coord1[3] = { Faces_Triangles[triangle_index], Faces_Triangles[triangle_index+1],Faces_Triangles[triangle_index+2]};
        float coord2[3] = {Faces_Triangles[triangle_index+3],Faces_Triangles[triangle_index+4],Faces_Triangles[triangle_index+5]};
    float coord3[3] = {Faces_Triangles[triangle_index+6],Faces_Triangles[triangle_index+7],Faces_Triangles[triangle_index+8]};
float *norm = this->calculateNormal( coord1, coord2, coord3 );


        float* Model_OBJ::calculateNormal( float *coord1, float *coord2, float *coord3 )
        {
           /* calculate Vector1 and Vector2 */
           float va[3], vb[3], vr[3], val;
           va[0] = coord1[0] - coord2[0];
           va[1] = coord1[1] - coord2[1];
           va[2] = coord1[2] - coord2[2];

           vb[0] = coord1[0] - coord3[0];
           vb[1] = coord1[1] - coord3[1];
           vb[2] = coord1[2] - coord3[2];

           /* cross product */
           vr[0] = va[1] * vb[2] - vb[1] * va[2];
           vr[1] = vb[0] * va[2] - va[0] * vb[2];
           vr[2] = va[0] * vb[1] - vb[0] * va[1];

           /* normalization factor */
           val = sqrt( vr[0]*vr[0] + vr[1]*vr[1] + vr[2]*vr[2] );

            float norm[3];
            norm[0] = vr[0]/val;
            norm[1] = vr[1]/val;
            norm[2] = vr[2]/val;


            return norm;
        }
Run Code Online (Sandbox Code Playgroud)

我有 2 个问题。

  1. 我如何知道法线是朝内还是朝外?.obj 文件中每行的顶点是否有某种顺序,指示如何计算法线?
  2. 在初始化函数中,他使用了GL_SMOOTH. GL_SMOOTH这是不正确的,因为如果使用而不是,我需要为每个顶点提供法线GL_FLAT

zer*_*298 5

问题1

glFrontFace决定风序

风序是指一组顶点应以什么顺序出现才能将法线视为正值。考虑下面的三角形。它的顶点是顺时针定义的。如果我们告诉 OpenGL glFrontFace(GL_CW)(顺时针方向意味着正面),那么法线基本上会从屏幕的正上方向您伸出,以便被视为“向外”。

附带说明一下,逆时针方向是默认值,您应该坚持使用。

无论如何,您需要真正定义法线,特别是如果您想在场景中进行任何照明,因为它们用于照明计算。 glFrontFace只是让你告诉 OpenGL 你想以哪种方式解释多边形的前面是什么。

在上面的例子和下面的图中,如果我们告诉 OpenGL 我们逆时针定义面,也定义glEnabledglCullFace并将其设置为 那么GL_BACK我们的三角形将不会显示,因为我们会看它的背面,而我们告诉 OpenGL 不要显示多边形的背面。

您可以在此处阅读有关面部剔除的更多信息:面部剔除

顺时针风的三角形

如果您不想自己创建法线,Wavefront.obj 支持在文件中声明法线。只需确保您的出口商添加它们即可。

另外,Wavefront 希望每个顶点都有一个法线定义:

f v1//vn1 v2//vn2 v3//vn3 ...
Run Code Online (Sandbox Code Playgroud)

其中vN是面的顶点fvnN是顶点的法线。通过为每个顶点提供法线,与通过定义每个面的法线或将同一面的所有顶点法线设置为相同的方式相比,您可以获得更平滑的表面。看一下这个问题,看看你可以在球体上做出什么改变:OpenGL: Why do I have to set a normal with glNormal?

如果您的 .obj 文件没有定义法线,我将使用面定义顺序并跨越定义面的两条边。考虑此处使用的方法:计算表面法线

编辑

我想我可能有点困惑。多边形的正面与其法线仅略有相关。法线仅真正用于照明计算。您不必拥有它们,但它们是计算物体亮度时使用的大变量之一。

我同时解释了多边形的“正面性”,因为在谈论凸多边形时,它有点有意义,即您的法线相对于形状会伸出三角形的“前面”你正在做。

如果您创建了一个巨大的洞穴,或者您的相机主要驻留在某个凹形内部,那么让您的法线指向内是有意义的,因为您的光源可能会从您的形状内部反射。

问题2

GL_SMOOTH确定您要使用哪种着色模型glShadeModel

GL_SMOOTH表示平滑着色,其中颜色实际上是在每个顶点之间插值的,vsGL_FLAT表示平坦着色,其中仅使用一种颜色。通常,您将使用默认值GL_SMOOTH

在任何一种情况下,您都不必每个顶点定义法线。然而,如果你想GL_SMOOTH看起来“不错”,你可能会想要这样做,因为它会在每个顶点之间渲染时进行插值,而不是仅仅选择一个顶点作为属性。


另外,请记住,每当您离开固定功能管道并开始使用着色器时,所有这些都会消失。