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

<p>本文經過包含許多頂點數據的複雜模型來比較使用glBegin()/glEnd當即模式,顯示列表,以及頂點索引數組的性能與內存。</p> <p>F-16 Thunderbird的飛機模型有3704個獨立的三角形,經過Deep Exporation工具的索引模式編制後,共有1898個獨立的頂點,2716個法線,2925個紋理座標。</p> <p><a href="http://static.oschina.net/uploads/img/201312/22132527_DQPt.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://static.oschina.net/uploads/img/201312/22132527_VHTe.png" width="469" height="369"></a></p> <p>下面代碼展現DrawBody函數,經過遍歷索引來爲每個獨立的三角形設置併發送紋理,法線和頂點座標。</p><pre class="prettyprint">void DrawBody(void) { int iFace, iPoint; glBegin(GL_TRIANGLES); for(iFace = 0; iFace &lt; 3074; iFace++) //遍歷每個三角形 for(iPoint = 0; iPoint &lt; 3; iPoint++) //每個頂點 { //設置紋理 glTexCoord2fv(textures[face_indices[iFace][iPoint+6]]); //設置法線 glNormal3fv(normals[face_indices[iFace][iPoint+3]]);git

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

}github

</pre> <p>當你必須優化模型的存儲空間時,這種方法是能夠的。例如你在嵌入式中須要節省內存,或者在網絡上傳輸時須要減小流量。但在實時應用中,這種方法的性能很是差,由於每一次都向OpenGL發送一個頂點數據,函數調用的次數也特別多。</p> <p>顯而易見的加速這些代碼執行的速度就是使用顯示列表的方式。咱們把這些代碼放到顯示列表中。</p> <p>glNewList(bodyList, GL_COMPILE);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DrawBody();<br>&nbsp;&nbsp;&nbsp; glEndList();</p> <p>….</p> <p>glCallList(bodyList);</p> <p>下面咱們來對比一下顯示列表的方式和頂點索引數組的方式。</p> <h4>計算花費</h4> <p>首先計算一下這些通過包裝過的頂點數據的所須要的內存。</p><pre class="prettyprint">// Thunderbird body extern short face_indicies[3704][9]; extern GLfloat vertices [1898][3]; extern GLfloat normals [2716][3]; extern GLfloat textures [2925][2]; </pre> <p>其中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 }....}</p> <p>face_indicies 須要 3074*9*sizeof(short), 55332字節.相似地計算出vertices要22776字節,normals 32592字節。textures 23400字節。總計134100 約130KB.</p> <p>在<strong>顯示列表</strong>中,咱們須要把這些數據拷貝一份到顯示列表(顯示列表中的命令和數據會通過優化後,放到命令緩衝區或者圖形硬件中)咱們無法計算顯示列表具體使用多少內存,但能夠對頂點的數據進行估算。每一個三角形須要3個頂點,3個法線,2個紋理座標,這些都是浮點數。假設sizeof(float)爲4個字節。那麼:</p> <p>3704*3=11112個頂點。每一個頂點包含3個成分(x,y,z)因此有11112*3=33336個浮點數值,同理法線有33336個浮點值,紋理座標有22224個浮點值。把這些加起來再乘以4個字節爲335,584個字節。那麼加上以前的原始數據有469684個字節 約460kb,不到0.5M.可是咱們有11,112個頂點數據須要通過OpenGL的變換管道,這裏麪包含了許多矩陣運算。</p> <h4>建立合理的頂點索引數組</h4> <p>上面所存儲的數據還不能直接用於OpenGL的頂點數組。由於OpenGL要求頂點數組,法線數組和紋理座標數組必須是一樣的大小,這樣數組的遍歷方式才能保持一致。頂點數組的第0個元素和法線數組的第0個元素是對應的。對於索引數組也有一樣的要求。</p> <p>在下面的例子中,咱們使用一個類來處理現有的數組,併爲其創建索引。下面是處理機身和玻璃座艙蓋並創建索引的代碼:</p><pre class="prettyprint">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(&amp;vNorms[iPoint][0], &amp;normals[face_indices[iFace][iPoint+3][0]], sizeof(M3DVector3f));

	memcpy(&amp;vTex[iPoint][0], &amp;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 &lt; 3; iPoint++)
{
		memcpy(&amp;vVerts[iPoint][0], &amp;verticesGlass[face_indiciesGlass[iFace][iPoint]][0], sizeof(M3DVector3f));
		memcpy(&amp;vNorms[iPoint][0], &amp;normalsGlass[face_indiciesGlass[iFace][iPoint+3]][0], sizeof(M3DVector3f)); 
		memcpy(&amp;vTex[iPoint][0], &amp;texturesGlass[face_indiciesGlass[iFace][iPoint+6]][0], sizeof(M3DVector2f));
}

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

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

</pre> <p>首先,咱們聲明瞭兩個三角形網格類</p> <p>CTriangleMesh thunderBirdBody; </p> <p>CTriangleMesh thunderBirdGalss; </p> <p>而後咱們要告訴包含全部頂點所須要的大小的最大值,在最壞的狀況下咱們可能有3074個惟一的頂點,但通常狀況下,許多頂點是共享的,值是同樣的。</p> <p>thunderBirdBody.BeginMesh(3074*3); </p> <p>而後遍歷集體全部的面,並收集每個獨立的三角形,並做爲AddTriangle的參數,AddTriagnle會組織索引數組。在AddTriangle函數中把傳進來的參數與以前的頂點數據進行比較看是否有重複的。若是是重複的在索引數組中就用同一個索引值。其內部處理代碼:</p><pre class="prettyprint"> for(GLuint iVertex = 0; iVertex &lt; 3; iVertex++) { GLuint iMatch = 0; for(iMatch = 0; iMatch &lt; nNumVerts; iMatch++) { // If the vertex positions are the same if(m3dCloseEnough(pVerts[iMatch][0], verts[iVertex][0], e) &amp;&amp; m3dCloseEnough(pVerts[iMatch][1], verts[iVertex][1], e) &amp;&amp; m3dCloseEnough(pVerts[iMatch][2], verts[iVertex][2], e) &amp;&amp;

// AND the Normal is the same...
           m3dCloseEnough(pNorms[iMatch][0], vNorms[iVertex][0], e) &amp;&amp;
           m3dCloseEnough(pNorms[iMatch][1], vNorms[iVertex][1], e) &amp;&amp;
           m3dCloseEnough(pNorms[iMatch][2], vNorms[iVertex][2], e) &amp;&amp;
               
            // And Texture is the same...
            m3dCloseEnough(pTexCoords[iMatch][0], vTexCoords[iVertex][0], e) &amp;&amp;
            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], &amp;vTexCoords[iVertex], sizeof(M3DVector2f));
        pIndexes[nNumIndexes] = nNumVerts;
        nNumIndexes++; 
        nNumVerts++;
        }   
    }

</pre> <h4>比較開銷</h4> <p>如今讓咱們來比較這三種渲染模型的方式的開銷。在CTriangleMesh類的統計中,Thunderbird的機身模型中共有3265個惟一的頂點(包含法線和紋理座標)和11,112個索引。每一個頂點和法線包含3個浮點值,紋理座標包含兩個浮點值。因此</p> <p>3265*8=26120個浮點值。再乘以4有104,480字節,再加上使用short類型建立的索引數組11,112*2=22,224字節。 總共有126,704個字節,約124kb</p> <p>對比表格:</p> <table border="2" cellspacing="0" cellpadding="2" width="600"> <tbody> <tr> <td valign="top" width="200">渲染模式</td> <td valign="top" width="200">內存使用量</td> <td valign="top" width="200">須要變換的頂點格式</td></tr> <tr> <td valign="top" width="200">當即模式</td> <td valign="top" width="200">約130kb</td> <td valign="top" width="200">11,112</td></tr> <tr> <td valign="top" width="200">顯示列表</td> <td valign="top" width="200">約460kb</td> <td valign="top" width="200">11,112</td></tr> <tr> <td valign="top" width="200">頂點索引數組</td> <td valign="top" width="200">約124kb</td> <td valign="top" width="200">3,265</td></tr></tbody></table> <p>從上面的表格能夠看出,頂點索引數組不但使用了更少的內存,並且僅僅須要處理其餘模式三分之一的頂點。若是模型有許多尖銳的角或邊那麼共享的頂點數就較少,若是模型是平滑的表面那麼共享的頂點數就多。使用頂點索引數組的方式可以極大的提高性能。</p> <p>源代碼:<a href="https://github.com/sweetdark/openglex/tree/master/thunderbird">https://github.com/sweetdark/openglex/tree/master/thunderbird</a></p>

相關文章
相關標籤/搜索