50

10000 頂点 (100x100) の三角形メッシュを描画しました。これは芝生の地面になります。gldrawelements() を使用しました。私は一日中見てきましたが、これの法線を計算する方法をまだ理解できません。各頂点には独自の法線がありますか、それとも各三角形には独自の法線がありますか? コードを編集して法線を組み込む方法について、誰かが正しい方向に向けることができますか?

struct vertices {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}vertices[10000];

GLuint indices[60000];

/*
99..9999
98..9998
........
01..9901
00..9900
*/

void CreateEnvironment() {
    int count=0;
    for (float x=0;x<10.0;x+=.1) {
        for (float z=0;z<10.0;z+=.1) {
            vertices[count].x=x;
            vertices[count].y=0;
            vertices[count].z=z;
            count++;
        }
    }
    count=0;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            GLuint v1=(a*100)+b;indices[count]=v1;count++;
            GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
            GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
        }
    }
    count=30000;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            indices[count]=(a*100)+b+100;count++;//9998
            indices[count]=(a*100)+b+1;count++;//9899
            indices[count]=(a*100)+b+101;count++;//9999
        }
    }
}

void ShowEnvironment(){
    //ground
    glPushMatrix();
    GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
    glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
    glEnableClientState(GL_VERTEX_ARRAY);
    glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );
    glVertexPointer(3,GL_FLOAT,0,vertices);
    glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
    glDisableClientState(GL_VERTEX_ARRAY);
    glPopMatrix();
}

EDIT 1ここに私が書いたコードがあります。ベクトルの代わりに配列を使用し、すべての法線を法線と呼ばれる構造体に保存しました。ただし、まだ機能しません。*indices で未処理の例外が発生します。

struct Normals {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}normals[20000];
Normals* normal = normals;
//***************************************ENVIRONMENT*************************************************************************
struct vertices {
    GLfloat x;
    GLfloat y;
    GLfloat z;
}vertices[10000];

GLuint indices[59403];

/*
99..9999
98..9998
........
01..9901
00..9900
*/

void CreateEnvironment() {
    int count=0;
    for (float x=0;x<10.0;x+=.1) {
        for (float z=0;z<10.0;z+=.1) {
            vertices[count].x=x;
            vertices[count].y=rand()%2-2;;
            vertices[count].z=z;
            count++;
        }
    }
    //calculate normals 
    GLfloat vector1[3];//XYZ
    GLfloat vector2[3];//XYZ
    count=0;
    for (int x=0;x<9900;x+=100){
        for (int z=0;z<99;z++){
            vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x
            vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
            vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
            vector2[0]= vertices[x+z+1].x-vertices[x+z+100].x;//vector2x
            vector2[1]= vertices[x+z+1].y-vertices[x+z+100].y;//vector2y
            vector2[2]= vertices[x+z+1].z-vertices[x+z+100].z;//vector2z
            normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
            normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
            normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
        }
    }
    count=10000;
    for (int x=100;x<10000;x+=100){
        for (int z=0;z<99;z++){
            vector1[0]= vertices[x+z].x-vertices[x+z+1].x;//vector1x -- JUST ARRAYS
            vector1[1]= vertices[x+z].y-vertices[x+z+1].y;//vector1y
            vector1[2]= vertices[x+z].z-vertices[x+z+1].z;//vector1z
            vector2[0]= vertices[x+z+1].x-vertices[x+z-100].x;//vector2x
            vector2[1]= vertices[x+z+1].y-vertices[x+z-100].y;//vector2y
            vector2[2]= vertices[x+z+1].z-vertices[x+z-100].z;//vector2z
            normals[count].x= vector1[1] * vector2[2]-vector1[2]*vector2[1];
            normals[count].y= vector1[2] * vector2[0] - vector1[0] * vector2[2];
            normals[count].z= vector1[0] * vector2[1] - vector1[1] * vector2[0];count++;
        }
    }

    count=0;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            GLuint v1=(a*100)+b;indices[count]=v1;count++;
            GLuint v2=(a*100)+b+1;indices[count]=v2;count++;
            GLuint v3=(a*100)+b+100;indices[count]=v3;count++;
        }
    }
    count=30000;
    for (GLuint a=0;a<99;a++){
        for (GLuint b=0;b<99;b++){
            indices[count]=(a*100)+b+100;count++;//9998
            indices[count]=(a*100)+b+1;count++;//9899
            indices[count]=(a*100)+b+101;count++;//9999
        }
    }
}

void ShowEnvironment(){
    //ground
    glPushMatrix();
    GLfloat GroundAmbient[]={0.0,0.5,0.0,1.0};
    GLfloat GroundDiffuse[]={1.0,0.0,0.0,1.0};
    glMaterialfv(GL_FRONT,GL_AMBIENT,GroundAmbient);
    glMaterialfv(GL_FRONT,GL_DIFFUSE,GroundDiffuse);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer( GL_FLOAT, 0, normal);
    glVertexPointer(3,GL_FLOAT,0,vertices);
    glDrawElements(GL_TRIANGLES,60000,GL_UNSIGNED_INT,indices);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glPopMatrix();
}
//***************************************************************************************************************************
4

6 に答える 6

143

各頂点には独自の法線がありますか、それとも各三角形には独自の法線がありますか?

多くの場合、答えは「場合による」です。法線は、特定の平面 (N 次元) 内のすべてのベクトルに垂直なベクトルとして定義されるため、法線を計算するには平面が必要です。頂点の位置は単なる点であり特異なので、法線を計算するには実際には面が必要です。したがって、法線計算の最初のステップは、面のエッジの外積を評価することによって面の法線を決定することであるため、単純に、面ごとに法線があると仮定することができます。

点ABCを持つ三角形があるとします。これらの点には位置ベクトル↑A↑B↑Cがあり、エッジにはベクトル↑B - ↑Aおよび↑C - ↑Aがあるため、面の法線ベクトルは↑です。 N f = (↑B - ↑A) × (↑C - ↑A)

上記の↑N fの大きさは、顔の面積に正比例することに注意してください。

滑らかなサーフェスでは、頂点は面間で共有されます (または、それらの面が頂点を共有していると言えます)。その場合、頂点の法線は、それが属する面の面法線の 1 つではなく、それらの線形結合です。

↑N v = ∑ p ↑N f ; ここで、pは各顔の重みです。

関係する面の法線間の重みが等しいと仮定することもできます。しかし、面が大きいほど、法線への影響が大きくなると考える方が理にかなっています。

↑v i = ↑v/|↑v|の逆長でスケーリングすることにより、ベクトル↑vで正規化することを思い出してください。. しかし、すでに述べたように、顔の法線の長さはすでに顔の面積に依存しています。したがって、上記の重み係数pは、ベクトル自体に既に含まれています: その長さ、別名マグニチュード。したがって、すべての面法線を合計するだけで、頂点法線ベクトルを取得できます。

照明の計算では、法線ベクトルは単位長でなければなりません。つまり、使用できるように正規化されている必要があります。まとめた後、新しく見つかった頂点法線を正規化し、それを使用します。

注意深い読者は、滑らかな表面が頂点を共有すると具体的に述べたことに気付いたかもしれません。実際、ジオメトリにいくつかの折り目/ハード エッジがある場合、両側の面は頂点を共有しません。OpenGL では、頂点は

  • 位置
  • 正常
  • (色)
  • N テクスチャ座標
  • M その他の属性

これらの 1 つを変更すると、まったく異なる頂点が得られます。現在、一部の 3D モデラーは、頂点をポイントの位置としてのみ見て、面ごとに残りの属性を保存しています (Blender はそのようなモデラーです)。これにより、メモリがいくらか節約されます (属性の数によっては、かなりの量のメモリが節約されます)。しかし、OpenGL はすべてを必要とするため、このような混合パラダイム ファイルを扱う場合は、最初にそれを OpenGL 互換データに分解する必要があります。PLY エクスポーターのような Blender のエクスポート スクリプトの 1 つを見て、それがどのように行われるかを確認してください。


次に、他のことをカバーします。あなたのコードにはこれがあります:

 glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );

インデックス ポインタは、頂点配列のインデックスとは何の関係もありません。これは、グラフィックがまだトゥルー カラーの代わりにパレットを使用していた時代からのアナクロシムです。ピクセルの色は、RGB 値を指定することによってではなく、限られた色のパレットにオフセットする単一の数値によって設定されました。パレットの色は、いくつかのグラフィック ファイル形式で引き続き使用できますが、それらを使用する適切なハードウェアはもうありません。

メモリとコードからglIndexPointer (および glIndex) を消去してください。それらはあなたが思っていることを実行しません インデックス付きカラー モード全体は使用するには難解です。率直に言って、1998 年以降に製造されたハードウェアでまだサポートされているものを知りませんそれ。

于 2011-07-12T08:13:09.373 に答える
27

頂点ごと。

外積を使用して、特定の頂点を囲む三角形の面法線を計算し、それらを加算して正規化します。

于 2011-07-11T20:58:26.663 に答える