TOTAL:177, TODAY:148

ドーナツを頂点バッファで高速化

前回は、頂点配列を使用して、トーラス(ドーナツ)を描画しましたが、今回は頂点バッファを使用します。頂点バッファは、あらかじめ頂点データをOpenGL側に作成しておくことができるので、CPUの負担が減り、モデルを描画するたびに、頂点データをOpenGL側に渡す(コピーする)必要がないため、高速な描画が可能となります。
頂点バッファは、OpenGL1.5で追加された機能であるため、wglGetProcAddress関数によるGL関数のポインタ取得が必要になります。実際のプログラムでは、次のようなマクロを使用して、頂点バッファ関連の4つの関数へのポインタを取得しています。
下記のようにwglGetProcAddressを使用しなくても、GLEWを使えば、OpenGL 1.2以降の機能を簡単に利用することが分かりました。インストール方法を知りたい方は、「GLEWを使ったOpenGL拡張」のページを見てください。但し、NVIDIAドライバーのインストールは必要です。

#include <GL/glext.h>
#include <GL/wglext.h>

#define GET_GL_PROCADDR(__funcptr, __funcname)\
    if ((__funcname = (__funcptr)wglGetProcAddress(#__funcname)) == 0)\
    {\
        fprintf(stderr, "%s functions is not found!!\n", #__funcname);\
        g_errGLProcAddr = -1;\
    }

    :   : 途中略 :   :

GET_GL_PROCADDR(PFNGLGENBUFFERSPROC, glGenBuffers);
GET_GL_PROCADDR(PFNGLBUFFERDATAPROC, glBufferData);
GET_GL_PROCADDR(PFNGLBUFFERSUBDATAPROC, glBufferSubData);
GET_GL_PROCADDR(PFNGLBINDBUFFERPROC, glBindBuffer);

頂点データの頂点バッファへの登録

頂点バッファで描画するためには、まずバッファオブジェクトを作成し、頂点を登録する必要があります。頂点には、位置座標だけでなく、法線やテクスチャ座標はもちろん、glDrawElementsで使用するインデックスも登録することができます。ここでは、位置、法線、カラー、テクスチャ座標、インデックスを登録しています。

/* 位置座標 */
glGenBuffers(1, &vtxID);
glBindBuffer(GL_ARRAY_BUFFER, vtxID);
glBufferData(GL_ARRAY_BUFFER, sizeof(torusVtx), torusVtx, GL_STATIC_DRAW);

/* 法線 */
glGenBuffers(1, &nrmID);
glBindBuffer(GL_ARRAY_BUFFER, nrmID);
glBufferData(GL_ARRAY_BUFFER, sizeof(torusNrm), torusNrm, GL_STATIC_DRAW);

    :    : 途中略 :    :

/* インデックス */
glGenBuffers(1, &idxID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(torusIdx), 
                                                torusIdx, GL_STATIC_DRAW);

頂点バッファはバッファオブジェクトと呼ばれており、テクスチャオブジェクトと似た登録方法になっています。頂点データ(位置、法線、カラー、テクスチャ座標等)の登録には、GL_ARAY_BUFFERを使用し、インデックスの登録には、GL_ELEMENT_ARRAY_BUFFERを使用することに注意してください。英語になってしまいますが、もっと詳しい説明は、OpenGLのリファレンスを参照してください。

頂点バッファによる描画

頂点バッファで描画するためには、次のようにglVertexPointer等をする前に、登録されたバッファオブジェクト(頂点バッファ)をglBindBufferでバインドする必要があります。

/* 位置座標の設定 */
glEnableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, vtxID);
glVertexPointer(3, GL_FLOAT, 0, 0);

/* 法線の設定 */
glEnableClientState(GL_NORMAL_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, nrmID);
glNormalPointer(GL_FLOAT, 0, 0);

    :    : 途中略 :    :

/* インデックスの設定 */
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, idxID);

/* 頂点配列による描画 */
offset = 0;
for (i = 0; i < torusNumPrim; i++)
{
    glDrawElements(GL_TRIANGLE_STRIP, torusNumPrimVtx, 
                        GL_UNSIGNED_SHORT, (void *)offset);
    offset += sizeof(unsigned short) * torusNumPrimVtx;
}

頂点配列では、glVertexPointer等の最後の引数に、頂点データへのポインタを与えていましたが、頂点バッファでは、バインドされている頂点バッファのどの部分から使用するかを示すオフセットを渡します。

頂点配列との速度比較

プログラムをコンパイルすれば、次のような絵が描画できると思います。「b」キーを押すことで、頂点配列と頂点バッファを切り替えることができます。

トーラスを使用して、頂点配列と頂点バッファの速度を比較しました。「文字描画と時間測定」で説明したように、ディスプレイの垂直同期をはずします。「t」キーを押すと、ウィンドウでの速度表示はオフにして、1024回の平均をDOS窓に出力するようにしました。ウィンドウに表示してもいいのですが、文字描画の部分が思ったよりも遅いため、こうしました。すると、次のような結果となり、私の環境では、フレームレート(Frame)が約1.75倍速くなっています。

VB : Frame=3087[fps]: Draw=  26007[fps]: Perf=213051029[tps] <-頂点バッファ
VB : Frame=3083[fps]: Draw=  26918[fps]: Perf=220513734[tps]
VB : Frame=3077[fps]: Draw=  25868[fps]: Perf=211912879[tps]
VB : Frame=3088[fps]: Draw=  26615[fps]: Perf=218030014[tps]
VB : Frame=3084[fps]: Draw=  26388[fps]: Perf=216167297[tps]
VA : Frame=1785[fps]: Draw=   2936[fps]: Perf=24051599[tps]  <-頂点配列
VA : Frame=1788[fps]: Draw=   2926[fps]: Perf=23970026[tps]
VA : Frame=1777[fps]: Draw=   2900[fps]: Perf=23754648[tps]
VA : Frame=1737[fps]: Draw=   2801[fps]: Perf=22944887[tps]
VA : Frame=1786[fps]: Draw=   2929[fps]: Perf=23991860[tps]

最新の7件

OpenGL

電子工作

玄箱HG

ホームページ

日記

Copyright (C) 2007 Arakin , All rights reserved.