OpenGL计算法线(四边形)

时间:2012-01-17 16:21:18

标签: c++ opengl normals

我的问题是关于OpenGL和Normals,我理解他们背后的数学,我取得了一些成功。

我在下面附加的函数接受交错的顶点数组,并计算每4个顶点的法线。这些代表具有相同方向的QUADS。根据我的理解,这4个顶点应该共享相同的法线。只要他们面对同样的方式。

我遇到的问题是我的QUADS是用对角线渐变渲染的,就像这样:Light Effect - 除了阴影位于中间,角落里有灯光。

我以一致的方式画出我的QUADS。 TopLeft,TopRight,BottomRight,BottomLeft和我用来计算法线的顶点是TopRight - TopLeft和BottomRight - TopLeft。

希望有人可以看到我犯了一个大错的东西,但我已经在这里待了好几个小时而没有占上风。

为了记录,我渲染了一个立方体,并在我的物体旁边放了一个茶壶来检查我的灯光是否正常工作,所以我很确定没有关于灯光位置的问题。

void CalculateNormals(point8 toCalc[], int toCalcLength)
{
    GLfloat N[3], U[3], V[3];//N will be our final calculated normal, U and V will be the subjects of cross-product
    float length;

for (int i = 0; i < toCalcLength; i+=4) //Starting with every first corner QUAD vertice
{
    U[0] = toCalc[i+1][5] - toCalc[i][5]; U[1] = toCalc[i+1][6] - toCalc[i][6]; U[2] = toCalc[i+1][7] - toCalc[i][7]; //Calculate Ux Uy Uz 
    V[0] = toCalc[i+3][5] - toCalc[i][5]; V[1] = toCalc[i+3][6] - toCalc[i][6]; V[2] = toCalc[i+3][7] - toCalc[i][7]; //Calculate Vx Vy Vz

    N[0] = (U[1]*V[2]) - (U[2] * V[1]);
    N[1] = (U[2]*V[0]) - (U[0] * V[2]);
    N[2] = (U[0]*V[1]) - (U[1] * V[0]);

    //Calculate length for normalising
    length = (float)sqrt((pow(N[0],2)) + (pow(N[1],2)) + (pow(N[2],2)));

    for (int a = 0; a < 3; a++)
    {
        N[a]/=length;
    }

    for (int j = 0; i < 4; i++)
    {
                    //Apply normals to QUAD vertices (3,4,5 index position of normals in interleaved array)
        toCalc[i+j][3] = N[0]; toCalc[i+j][4] = N[1]; toCalc[i+j][5] = N[2];
    }
}
}

3 个答案:

答案 0 :(得分:2)

看起来你正在将顶点位置值用于索引5,6和7的计算,然后在索引3,4和5处写出法线。注意索引5是如何在两者上使用的。我想其中一个不正确。

答案 1 :(得分:2)

看起来你的for-loops正在咬你。

for (int i = 0; i < toCalcLength; i+=4) //Starting with every first corner QUAD vertice
{
  ...
  for (int j = 0; i < 4; i++)
  { //            ^      ^
    // Should you be using 'j' instead of 'i' here?
    // j will never increment
    // This loop won't be called at all after the first time through the outer loop
    ...
  }
}

答案 2 :(得分:2)

您使用索引3,4和 5 来存储普通:

toCalc[i+j][3] = N[0]; toCalc[i+j][4] = N[1]; toCalc[i+j][5] = N[2];

AND 您使用索引 5 ,6和7来获取点坐标:

U[0] = toCalc[i+1][5] - toCalc[i][5]; U[1] = toCalc[i+1][6] - toCalc[i][6]; U[2] = toCalc[i+1][7] - toCalc[i][7];

这些索引重叠(normal.x与position.z共享相同的索引),这不应该发生。


建议:

  1. 把所有东西都放进结构中。
  2. 或者:
    1. 使用数学库。
    2. 或将矢量算术放入单独的适当命名的子程序中。
  3. 使用命名变量而不是索引。
  4. 通过这样做,您将减少代码中的错误数量。 a.position.xquad[0][5]更容易阅读,并且在未复制粘贴代码时更容易修复向量操作中的拼写错误。


    您可以使用联合来按索引和名称访问矢量组件:

    struct Vector3{
        union{
            struct{
                float x, y, z;
            };
            float v[3];
        };
    };    
    

    用于计算四边形ABCD中的法线

    A--B
    |  |
    C--D
    

    使用公式:

    normal = normalize((B.position - A.position)X(C.position - A.position))。

    OR

    normal = normalize((D.position - A.position)X(C.position - B.position))。

    其中“X”表示“交叉产品”。

    无论哪种方式都可以。