c++ - 計算三角形網格中的法線




opengl computational-geometry (2)

我畫了一個有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();
}

編輯1這是我寫的代碼。 我只使用數組而不是向量,並將所有法線存儲在名為normals的結構中。 但它仍然不起作用。 我在* 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();
}
//***************************************************************************************************************************

每個頂點是否有自己的法線或每個三角形都有自己的法線?

像往常一樣,答案是:“這取決於”。 由於法線被定義為垂直於給定平面內所有向量的向量(在N維中),因此需要一個平面來計算法線。 頂點位置只是一個點,因此是單數,所以你實際上需要一個面來計算法線。 因此,天真地,可以假設法線是每個面,因為正常計算的第一步是通過評估面邊緣的叉積來確定面法線。

假設你有一個帶有點ABC的三角形,那麼這些點有位置向量↑A↑B↑C和邊有矢量↑B - ↑A↑C - ↑A所以面法線向量是↑ N f =(↑B - ↑A)×(↑C - ↑A)

請注意,如上所述, ↑N f的大小與面部區域成正比。

在平滑曲面中,頂點在面之間共享(或者您可以說這些面共享一個頂點)。 在這種情況下,頂點處的法線不是它所屬面的面法線之一,而是它們的線性組合:

↑N v =Σp↑N f ; 其中p是每個面的權重。

可以假設參與面法線之間具有相等的權重。 但是假設面部越大,它對正常的貢獻越大就越有意義。

現在回想一下你用矢量↑v進行標準化,然後用它的接收長度縮放它: ↑v i =↑v / |↑v | 。 但正如已經說過的,臉部法線的長度已經取決於臉部的面積。 因此,上面給出的加權因子p已經包含在向量本身中:它的長度,即大小。 因此,我們可以通過簡單地總結所有面法線來獲得頂點法線向量。

在照明計算中,法向量必須是單位長度,即歸一化為可用。 總結之後,我們將新發現的頂點法線標準化並使用它。

細心的讀者可能已經註意到我特意說光滑表面共享頂點。 事實上,如果幾何體中有一些摺痕/硬邊,那麼兩側的面不會共享頂點。 在OpenGL中,頂點是整個組合

  • 位置
  • 正常
  • (顏色)
  • N紋理坐標
  • M進一步的屬性

你改變其中一個,你有一個完全不同的頂點。 現在一些3D建模者只將頂點看作一個點的位置,並且每個面都存儲其餘的屬性(Blender就是這樣一個建模者)。 這節省了一些內存(或相當大的內存,具體取決於屬性的數量)。 但OpenGL需要全部內容,因此如果使用這樣的混合範例文件,您必須首先將其分解為OpenGL兼容數據。 看看Blender的一個導出腳本,比如PLY導出器,看看它是如何完成的。

現在來介紹一些其他的事情。 在你的代碼中你有這個:

 glIndexPointer( GL_UNSIGNED_BYTE, 0, indices );

索引指針與頂點數組索引無關 ! 這是從日子開始的anachronsim,當時圖形仍然使用調色板而不是真彩色。 通過給出RGB值來設置像素顏色,但是通過偏移到有限的顏色調色板中的單個數字來設置像素顏色。 調色板顏色仍然可以在幾種圖形文件格式中找到,但是沒有像樣的硬件使用它們了。

請從你的內存和你的代碼中刪除glIndexPointer (和glIndex),它們沒有按你認為的那樣做。整個索引顏色模式是使用的晦澀,坦率地說我不知道1998年以後仍然支持的任何硬件它。


每個頂點。

使用交叉積來計算給定頂點周圍三角形的面法線,將它們加在一起並進行標準化。