歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> OpenGL超級寶典學習筆記——性能比較

OpenGL超級寶典學習筆記——性能比較

日期:2017/3/1 9:32:12   编辑:Linux編程

本文通過包含許多頂點數據的復雜模型來比較使用glBegin()/glEnd立即模式,顯示列表,以及頂點索引數組的性能與內存。

F-16 Thunderbird的飛機模型有3704個獨立的三角形,通過Deep Exporation工具的索引模式編制後,共有1898個獨立的頂點,2716個法線,2925個紋理坐標。

下面代碼展示DrawBody函數,通過遍歷索引來為每一個獨立的三角形設置並發送紋理,法線和頂點坐標。

void DrawBody(void)

{

int iFace, iPoint;
glBegin(GL_TRIANGLES);
    for(iFace = 0; iFace < 3074; iFace++) //遍歷每一個三角形
        for(iPoint = 0; iPoint < 3; iPoint++) //每一個頂點
        {
            //設置紋理
            glTexCoord2fv(textures[face_indices[iFace][iPoint+6]]);
            //設置法線
            glNormal3fv(normals[face_indices[iFace][iPoint+3]]);

            //設置頂點
            glVertex3fv(vertices[face_indices[iFace][iPoint]]);
        }
glEnd();

}

當你必須優化模型的存儲空間時,這種方法是可以的。例如你在嵌入式中需要節省內存,或者在網絡上傳輸時需要減少流量。但在實時應用中,這種方法的性能非常差,因為每一次都向OpenGL發送一個頂點數據,函數調用的次數也特別多。

顯而易見的加速這些代碼執行的速度就是使用顯示列表的方式。我們把這些代碼放到顯示列表中。

glNewList(bodyList, GL_COMPILE);
DrawBody();
glEndList();

….

glCallList(bodyList);

下面我們來對比一下顯示列表的方式和頂點索引數組的方式。

計算花費

首先計算一下這些經過包裝過的頂點數據的所需要的內存。

// Thunderbird body

extern short face_indicies[3704][9];
extern GLfloat vertices [1898][3];
extern GLfloat normals [2716][3];
extern GLfloat textures [2925][2];

其中face_indicies包含了頂點,法線,和紋理的索引 short face_indicies[3704][9] = {{6,8,7 ,0,1,2 ,0,1,2 }, {6,9,8 ,0,3,1 ,0,3,1 }, {10,8,11 ,4,1,5 ,4,1,5 }....}

face_indicies 需要 3074*9*sizeof(short), 55332字節.類似地計算出vertices要22776字節,normals 32592字節。textures 23400字節。總計134100 約130KB.

顯示列表中,我們需要把這些數據拷貝一份到顯示列表(顯示列表中的命令和數據會經過優化後,放到命令緩沖區或者圖形硬件中)我們沒法計算顯示列表具體使用多少內存,但可以對頂點的數據進行估算。每個三角形需要3個頂點,3個法線,2個紋理坐標,這些都是浮點數。假設sizeof(float)為4個字節。那麼:

3704*3=11112個頂點。每個頂點包含3個成分(x,y,z)所以有11112*3=33336個浮點數值,同理法線有33336個浮點值,紋理坐標有22224個浮點值。把這些加起來再乘以4個字節為335,584個字節。那麼加上之前的原始數據有469684個字節 約460kb,不到0.5M.但是我們有11,112個頂點數據需要經過OpenGL的變換管道,這裡面包含了許多矩陣運算。

創建合理的頂點索引數組

上面所存儲的數據還不能直接用於OpenGL的頂點數組。因為OpenGL要求頂點數組,法線數組和紋理坐標數組必須是同樣的大小,這樣數組的遍歷方式才能保持一致。頂點數組的第0個元素和法線數組的第0個元素是對應的。對於索引數組也有同樣的要求。

在下面的例子中,我們使用一個類來處理現有的數組,並為其建立索引。下面是處理機身和玻璃座艙蓋並建立索引的代碼:

CTriangleMesh thunderBirdBody;

CTriangleMesh thunderBirdGalss;

//臨時空間
M3DVector3f vVerts[3];
M3DVector3f vNorms[3];
M3DVector2f vTex[3];

//開始收集機身的網格,設置最大值
thunderBirdBody.BeginMesh(3074*3);

//循環所有面
for(int iFace = 0; iFace < 3074; iFace++)
{

for(int iPoint = 0; iPoint < 3; iPoint++)
{
    memcpy(&vVerts[iPoint][0], &vertices[face_indices[iFace][iPoint][0]], sizeof(M3DVector3f));

    memcpy(&vNorms[iPoint][0], &normals[face_indices[iFace][iPoint+3][0]], sizeof(M3DVector3f));

    memcpy(&vTex[iPoint][0], &textures[face_indices[iFace][iPoint+6][0]], sizeof(M2DVector2f));
}
thunderBirdBody.AddTriangle(vVerts, vNorms, vTex);

}
//結束,並縮放頂點的值,以便屏幕的顯示。
thunderBirdBody.EndMesh();
thunderBirdBody.Scale(fScale);

thunderBirdGlass.BeginMesh(352*3);

for(int iFace = 0; iFace < 352; iFace++)
{

for(int iPoint = 0; iPoint < 3; iPoint++)
{
        memcpy(&vVerts[iPoint][0], &verticesGlass[face_indiciesGlass[iFace][iPoint]][0], sizeof(M3DVector3f));
        memcpy(&vNorms[iPoint][0], &normalsGlass[face_indiciesGlass[iFace][iPoint+3]][0], sizeof(M3DVector3f)); 
        memcpy(&vTex[iPoint][0], &texturesGlass[face_indiciesGlass[iFace][iPoint+6]][0], sizeof(M3DVector2f));
}

thunderBirdGlass.AddTriangle(vVerts, vNorms, vTex);
}

thunderBirdGlass.EndMesh();
thunderBirdGlass.Scale(fScale);

首先,我們聲明了兩個三角形網格類

CTriangleMesh thunderBirdBody;

CTriangleMesh thunderBirdGalss;

然後我們要告訴包含所有頂點所需要的大小的最大值,在最壞的情況下我們可能有3074個唯一的頂點,但一般情況下,許多頂點是共享的,值是一樣的。

thunderBirdBody.BeginMesh(3074*3);

然後遍歷集體所有的面,並收集每一個獨立的三角形,並作為AddTriangle的參數,AddTriagnle會組織索引數組。在AddTriangle函數中把傳進來的參數與之前的頂點數據進行比較看是否有重復的。如果是重復的在索引數組中就用同一個索引值。其內部處理代碼:

for(GLuint iVertex = 0; iVertex < 3; iVertex++)

    {
    GLuint iMatch = 0;
    for(iMatch = 0; iMatch < nNumVerts; iMatch++)
        {
        // If the vertex positions are the same
        if(m3dCloseEnough(pVerts[iMatch][0], verts[iVertex][0], e) &&
           m3dCloseEnough(pVerts[iMatch][1], verts[iVertex][1], e) &&
           m3dCloseEnough(pVerts[iMatch][2], verts[iVertex][2], e) &&

           // AND the Normal is the same...
           m3dCloseEnough(pNorms[iMatch][0], vNorms[iVertex][0], e) &&
           m3dCloseEnough(pNorms[iMatch][1], vNorms[iVertex][1], e) &&
           m3dCloseEnough(pNorms[iMatch][2], vNorms[iVertex][2], e) &&

            // And Texture is the same...
            m3dCloseEnough(pTexCoords[iMatch][0], vTexCoords[iVertex][0], e) &&
            m3dCloseEnough(pTexCoords[iMatch][1], vTexCoords[iVertex][1], e))
            {
            // Then add the index only
            pIndexes[nNumIndexes] = iMatch;
            nNumIndexes++;
            break;
            }
        }

    // No match for this vertex, add to end of list
    if(iMatch == nNumVerts)
        {
        memcpy(pVerts[nNumVerts], verts[iVertex], sizeof(M3DVector3f));
        memcpy(pNorms[nNumVerts], vNorms[iVertex], sizeof(M3DVector3f));
        memcpy(pTexCoords[nNumVerts], &vTexCoords[iVertex], sizeof(M3DVector2f));
        pIndexes[nNumIndexes] = nNumVerts;
        nNumIndexes++; 
        nNumVerts++;
        }   
    }

比較開銷

現在讓我們來比較這三種渲染模型的方式的開銷。在CTriangleMesh類的統計中,Thunderbird的機身模型中共有3265個唯一的頂點(包含法線和紋理坐標)和11,112個索引。每個頂點和法線包含3個浮點值,紋理坐標包含兩個浮點值。所以

3265*8=26120個浮點值。再乘以4有104,480字節,再加上使用short類型創建的索引數組11,112*2=22,224字節。 總共有126,704個字節,約124kb

對比表格:

渲染模式 內存使用量 需要變換的頂點格式 立即模式 約130kb 11,112 顯示列表 約460kb 11,112 頂點索引數組 約124kb 3,265

從上面的表格可以看出,頂點索引數組不但使用了更少的內存,而且僅僅需要處理其他模式三分之一的頂點。如果模型有許多尖銳的角或邊那麼共享的頂點數就較少,如果模型是平滑的表面那麼共享的頂點數就多。使用頂點索引數組的方式能夠極大的提升性能。

OpenGL超級寶典 第4版 中文版PDF+英文版+源代碼 見 http://www.linuxidc.com/Linux/2013-10/91413.htm

OpenGL編程指南(原書第7版)中文掃描版PDF 下載 http://www.linuxidc.com/Linux/2012-08/67925.htm

OpenGL 渲染篇 http://www.linuxidc.com/Linux/2011-10/45756.htm

Ubuntu 13.04 安裝 OpenGL http://www.linuxidc.com/Linux/2013-05/84815.htm

OpenGL三維球體數據生成與繪制【附源碼】 http://www.linuxidc.com/Linux/2013-04/83235.htm

Ubuntu下OpenGL編程基礎解析 http://www.linuxidc.com/Linux/2013-03/81675.htm

如何在Ubuntu使用eclipse for c++配置OpenGL http://www.linuxidc.com/Linux/2012-11/74191.htm

更多《OpenGL超級寶典學習筆記》相關知識 見 http://www.linuxidc.com/search.aspx?where=nkey&keyword=34581

Copyright © Linux教程網 All Rights Reserved