计算程序形状的法线

Calculate Normals for Procedural Shape

本文关键字:计算程序      更新时间:2023-10-16

给定下面的代码。取自 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的出色回答,我将努力解决我剩余的问题。也非常感谢巴特!

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

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

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

  • 目前尚不清楚法线是否初始化为零向量。
  • 您是在分配,而不是将法线添加到顶点法线。(因此,步骤 2 无法正常工作。此外,要添加到顶点法线的法线本身并未归一化。(叉积不会自动归一化 -- 必须归一化叉积的结果。
  • 您根本没有执行第 3 步。

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