计算程序形状的法线

时间:2012-06-06 21:44:32

标签: c++ opengl geometry

鉴于以下代码。取自http://pastie.org/pastes/764327/text

    void CreateSphere(int PointRows, int PointsPerRow)
{
    NumVertices = (PointRows-2)*PointsPerRow + 2;
    Vertices = new SVertex[NumVertices];
    IndexVect.clear();  //to be sure it is empty
    float x,y,z;
    int i,j;
    double r;
    for (i = 1; i < (PointRows-1); i++)
    {
        for (j = 0; j < PointsPerRow; j++)
        {
            y = 1.0 - float(i) / float(PointRows-1)*2.0;
            r = sin (acos(y));  //radius of the row
            x = r * sin(float(j) / float(PointsPerRow)*PI*2.0);     
            z = r * cos(float(j) / float(PointsPerRow)*PI*2.0);
        Vertices[(i-1)*PointsPerRow+j].x = x;
        Vertices[(i-1)*PointsPerRow+j].y = y;
        Vertices[(i-1)*PointsPerRow+j].z = z;
        Vertices[(i-1)*PointsPerRow+j].r = (float)(i) / float(PointRows);
        Vertices[(i-1)*PointsPerRow+j].g = 0.7;
        Vertices[(i-1)*PointsPerRow+j].b = (float)(j) / float(PointsPerRow);
    }

}
//The highest and deepest vertices:
Vertices[(PointRows-2)*PointsPerRow].x = 0.0;
Vertices[(PointRows-2)*PointsPerRow].y = 1.0;
Vertices[(PointRows-2)*PointsPerRow].z = 0.0;
Vertices[(PointRows-2)*PointsPerRow].r = 1.0;
Vertices[(PointRows-2)*PointsPerRow].g = 0.7;
Vertices[(PointRows-2)*PointsPerRow].b = 1.0;
Vertices[(PointRows-2)*PointsPerRow+1].x = 0.0;
Vertices[(PointRows-2)*PointsPerRow+1].y = -1.0;
Vertices[(PointRows-2)*PointsPerRow+1].z = 0.0;
Vertices[(PointRows-2)*PointsPerRow+1].r = 1.0;
Vertices[(PointRows-2)*PointsPerRow+1].g = 0.7;
Vertices[(PointRows-2)*PointsPerRow+1].b = 1.0;

for (i = 1; i < (PointRows-2); i++)
{
    for (j = 0; j < (PointsPerRow-1); j++)
    {
        IndexVect.push_back((i-1)*PointsPerRow+j);
        IndexVect.push_back((i-1)*PointsPerRow+j+1);
        IndexVect.push_back((i)*PointsPerRow+j);

        IndexVect.push_back((i-1)*PointsPerRow+j+1);
        IndexVect.push_back((i)*PointsPerRow+j+1);
        IndexVect.push_back((i)*PointsPerRow+j);
    }

    IndexVect.push_back((i-1)*PointsPerRow+PointsPerRow-1);
    IndexVect.push_back((i-1)*PointsPerRow);
    IndexVect.push_back((i)*PointsPerRow+j);

    IndexVect.push_back((i)*PointsPerRow);
    IndexVect.push_back((i-1)*PointsPerRow);
    IndexVect.push_back((i)*PointsPerRow+j);
}       

//The triangles to the highest and deepest vertices:
for (j = 0; j< (PointsPerRow-1); j++)
{
    IndexVect.push_back(j);
    IndexVect.push_back(j+1);
    IndexVect.push_back((PointRows-2)*PointsPerRow);
}
IndexVect.push_back(j);
IndexVect.push_back(0);
IndexVect.push_back((PointRows-2)*PointsPerRow);

for (j = 0; j< (PointsPerRow-1); j++)
{
    IndexVect.push_back((PointRows-3)*PointsPerRow+j);
    IndexVect.push_back((PointRows-3)*PointsPerRow+j+1);
    IndexVect.push_back((PointRows-2)*PointsPerRow+1);
}
IndexVect.push_back((PointRows-3)*PointsPerRow+j);
IndexVect.push_back((PointRows-3)*PointsPerRow);
IndexVect.push_back((PointRows-2)*PointsPerRow+1);
Indices = new GLuint[IndexVect.size()];  //allocate the required memory
for (i = 0; i < IndexVect.size(); i++)
{
    Indices[i] = IndexVect[i];
}
NumIndices = IndexVect.size();
IndexVect.clear();  //no longer needed, takes only memory

}

如何使用结果顶点计算法线....?

然后我希望使用glEnableClientState(GL_NORMAL_ARRAY)和glNormalPointer(GL_FLOAT,0,Normals)以及glDrawElements来将结果索引与法线一起绘制。

我有一个去,但它只是看起来不对劲。灯光出现在球体的左侧,而不是下方。

道歉。我没有在这里全面了解。基本上我试图通过在数据中输入噪声来生成随机形状。

如果我在第一个嵌套循环的末尾使用以下代码:

Normals[(i-1)*PointsPerRow+j].x = x;
Normals[(i-1)*PointsPerRow+j].y = y;
Normals[(i-1)*PointsPerRow+j].z = z;

我能够为球体生成正确的法线,一切看起来都很好。

但是,如果我这样做:

x=x+(noise3(x,y,z));
y=y+(noise3(x,y,z));
z=z+(noise3(x,y,z));

然后尝试使用:

Normals[(i-1)*PointsPerRow+j].x = x;
Normals[(i-1)*PointsPerRow+j].y = y;
Normals[(i-1)*PointsPerRow+j].z = z;

某些面孔的法线看起来不对。一旦创建完整的形状,我想循环遍历结果索引,然后计算每个点的法线,如果这有任何意义....?

好的,这就是我得到的。它似乎不起作用。它可能完全错误,所以很容易。

  for (j = 0; j < NumIndices-2; j=j+3)
   {

Ax = Vertices[Indices[j]].x;    Bx = Vertices[Indices[j+1]].x;   Cx = Vertices[Indices[j+2]].x;
Ay = Vertices[Indices[j]].y;   By = Vertices[Indices[j+1]].y;   Cy = Vertices[Indices[j+2]].y;
Az = Vertices[Indices[j]].z;   Bz = Vertices[Indices[j+1]].z;   Cz = Vertices[Indices[j+2]].z;

dms::Vector3 p1(Ax,Ay,Az);
dms::Vector3 p2(Bx,By,Bz);
dms::Vector3 p3(Cx,Cy,Cz);
dms::Vector3 V1= (p2 - p1);
dms::Vector3 V2 = (p3 - p1);
dms::Vector3 normal = V1.cross(V2);

Normals[j].x = normal[0];
Normals[j].y = normal[1];
Normals[j].z = normal[2];;

Normals[j+1].x = normal[0];
Normals[j+1].y = normal[1];
Normals[j+1].z = normal[2];;


Normals[j+2].x = normal[0];
Normals[j+2].y = normal[1];
Normals[j+2].z = normal[2];;

}

编辑---

通过重新安排计算2个向量的部分,我能够获得更好的结果。我更改了以下内容:

dms::Vector3 V1= (p2 - p1);
dms::Vector3 V2 = (p3 - p1);

要:

dms::Vector3 V1= (p2 - p1);
dms::Vector3 V2 = (p1 - p3);

它仍然看起来不太正确,但是沿着球体的前部有一条暗条,并且球体的顶部看起来很奇怪。

非常感谢Kaganar的出色答案,我将努力解决我的遗留问题。非常感谢Bart!

1 个答案:

答案 0 :(得分:4)

根据评论,这是一个可能的答案:

  • 步骤1:将所有顶点法线设置为零向量。
  • 步骤2:计算每个三角形的法线并将其添加到每个三角形的所有顶点。
  • 步骤3:规范化所有顶点法线。

关于您在撰写本答案时发布的代码:

  • 不清楚法线是否初始化为零向量。
  • 您正在分配,而不是将法线添加到顶点法线。 (因此,步骤2无法正常工作。)此外,您添加到顶点法线的法线本身不会被标准化。 (交叉产品不会自动标准化 - 您必须规范化交叉产品的结果。)
  • 你根本没有做第3步。

进一步说明:当您进行规范化时,请注意传递给规范化函数的接近零长度的向量,尤其是在使用噪声生成的形状时。由于归一化只是除了向量长度的分量,因此在进行通常期望的除法之前检查这个长度是否接近于零是很常见的。如果它接近零,那么这种标准化函数的输出通常只是向上矢量(因为在大多数着色模型中,零向量会导致虚假的暗结果。)