日別アーカイブ: 2009年9月6日

OpenGL のはじめ方 (2) wgl

Windows で OpenGL を使う方法がだんだんわかってきました。
DirectX ならとりあえず新しい SDK を入れれば終わりですが、OpenGL の場合は少々違います。
特別な SDK や lib が無くても、ドライバさえ対応していれば使うことができます。

敢えて OpenGL の各種ライブラリは使用していません。便利で容易に任せられる反面、中で何を
やっているのか、最新バージョンを使うにはどうしたらいいのかがわかりにくかったからです。

(1) OS と OpenGL のやりとりは WGL が行う。
(2) Windows SDK には OpenGL 1.1 のヘッダ&lib のみ。最小限。
(3) 新しい OpenGL に対応している GPU ライバをインストールする。
(4) ヘッダファイルは OpenGL Registry からダウンロードできる。
(5) wglGetProcAddress() を使って自分で最新の API を取得する。

API の取得は以前行ったとおり。ここ数日、実際に OpenGL 3.2 で追加された機能を
試すことが出来ました。今度は初期化部分、wgl まわりをもう少し詳しく調べてみます。

MSDN Using OpenGL on Windows NT/2000 and Windows 95/98

●ピクセルフォーマットと初期化

Direct3D と同じように、とりあえず Win32 API でウィンドウを作っておきます。
OpenGL の Context (HGLRC) を作るには HDC が必要です。

そのまま wglCreateContext() を呼び出してもエラーになります。
OpenGL が対応しているピクセルフォーマットになっていないことが原因でした。
あらかじめフレームバッファのフォーマットを指定しておきます。

現在対応しているフォーマットは下記の方法で取得可能です。

HDC hDC= GetDC( hWnd );

// 現在対応しているフォーマットの数を参照する
int format_count= DescribePixelFormat( hDC, 0, 0, NULL );

// 列挙する
for( int fi= 1 ; fi<= format_count ; fi++ ){
    PIXELFORMATDESCRIPTOR   pformat;
    DescribePixelFormat( hDC, fi, sizeof(PIXELFORMATDESCRIPTOR), &pformat );
}

GeForce 190.57 で実際に列挙してみました。126個あります。

GeForce 190.57 PIXELFORMATDESCRIPTOR 一覧

最後の方に W と GL 両方付いているものが Window 描画対応かつ OpenGL で使える
フォーマットです。

MSDN Choosing and Setting a Best-Match Pixel Format の例では
Color=24, Depth=32 を指定していますが、Direct3D を考えても、上の列挙の結果を見ても、
あまり一般的とはいえません。

今回は下記のピクセルフォーマット指定を行いました。
ChoosePixelFormat() を使うとフォーマットのマッチングを自動で行ってくれるようです。

static PIXELFORMATDESCRIPTOR   pformat= {
    sizeof(PIXELFORMATDESCRIPTOR),
    1,
    0
    |PFD_DRAW_TO_WINDOW
    |PFD_SUPPORT_OPENGL
    |PFD_DOUBLEBUFFER
    ,
    PFD_TYPE_RGBA,
    32,     // color
    0, 0,   // R
    0, 0,   // G
    0, 0,   // B
    0, 0,   // A
    0, 0, 0, 0, 0,      // AC R G B A
    24,     // depth
    8,      // stencil
    0,      // aux
    0,      // layertype
    0,  // reserved
    0,  // layermask
    0,  // visiblemask
    0   // damagemask
};

HDC hDC= GetDC( hWnd );

// ピクセルフォーマットの選択
int pfmt= ChoosePixelFormat( hDC, &pformat );
SetPixelFormat( hDC, pfmt, &pformat );

// OpenGL コンテキストの作成
HGLRC hGLRC= wglCreateContext( hDC );
wglMakeCurrent( hDC, hGLRC );

エラー判定を省いていますが、初期化手順は上記の通りです。
hDC, hGLRC は保存しておきます。終了時のコードは下記の通りです。

if( hGLRC ){
    wglMakeCurrent( NULL, NULL );
    wglDeleteContext( hGLRC );
    hGLRC= NULL;
}
if( hDC ){
    ReleaseDC( hWnd, hDC );
    hDC= NULL;
}

●描画ループ

wglMakeCurrent() で hGLRC を指定すれば、以後描画のために gl ~ API を
用いることが出来ます。Double Buffer を指定したので毎フレームの描画の最後に
SwapBuffers() を入れます。Direct3D の Present() に相当します。

    // フレームバッファクリア
    glClearColor( 0.0f, 0.0f, 0.3, 0.0f );
    glClearDepth( 1.0f );
    glClearStencil( 0 );
    glClear( GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT );

    // 描画など
    ~

    // flip
    SwapBuffers( hDC );

●新しい Context の作成

基本的な WGL と OpenGL の関係は上記の通りです。

さらに新しい API を使う場合、いくつか追加の手順が必要となります。
まず拡張 API にはいくつか種類があります。

・新しいバージョンの OpenGL core API
・OpenGL 拡張 API
・WGL 拡張 API

これら拡張 API を使う方法はどれも同じです。ヘッダファイルと対応ドライバを手に入れたなら、
あとは wglGetProcAddress() を用いるだけ。
wglGetProcAddress() 関数は、wglCreateContext() のあと wglMakeCurrent() で
hGLRC を設定した後でないと呼び出すことが出来ません。

(1) OpenGL Registry から wglext.h をダウンロードしておきます。
(2) 通常の手段で Context を作成し wglMakeCurrent() しておきます。
(3) WGL の拡張 API 、wglCreateContextAttribsARB() を取り出します。
(4) wglCreateContextAttribsARB() を使って HGLRC を作り直します。

HDC hDC= GetDC( hWnd );

// ピクセルフォーマットの選択
int pfmt= ChoosePixelFormat( hDC, &pformat );
SetPixelFormat( hDC, pfmt, &pformat );

// OpenGL コンテキストの作成
HGLRC hGLRC= wglCreateContext( hDC );
wglMakeCurrent( hDC, hGLRC );

// API 取り出し --- (A)
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB= (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( "wglCreateContextAttribsARB" );

// 使用する OpenGL のバージョンとプロファイルの指定
static const int  att[]= {
   WGL_CONTEXT_MAJOR_VERSION_ARB,   3,
   WGL_CONTEXT_MINOR_VERSION_ARB,   2,
   WGL_CONTEXT_FLAGS_ARB,           0,
   WGL_CONTEXT_PROFILE_MASK_ARB,    WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
   0,
};

// 新しい HGLRC の作成
HGLRC   hglrc= wglCreateContextAttribsARB( hDC, NULL, att );
wglMakeCurrent( hDC, hglrc );

// 古い HGLRC の削除と置き換え
wglDeleteContext( hGLRC );
hGLRC= hglrc;

// 新しい Core API の取り出し
InitOpenGL();

InitOpenGL() については こちら を参照してください。

なお GeForce で試している分には、特にこの手順を踏まなくても OpenGL 3.2 の命令が
使えるし描画もできています。

上の例では wglCreateContextAttribsARB() だけ個別に取り出していますが、
実際は InitOpenGL() (GLFunction) のテーブルに、gl ~ と同じように wgl 関数
を組み込みました。よって上の (A) のタイミングで InitOpenGL() を用いて
wglCreateContextAttribsARB を含めた一度にすべての拡張関数を取り出しています。

●プロファイルと互換性

OpenGL 3.1~ は DirectX のように、徐々に古い固定機能を無くし新しい機能への
置き換えが進んでいます。それでもアプリケーションが困らないように、従来と互換性を保った
API を使う手段も残されています。

・OpenGL 3.1 + GL_ARB_compatibility
・OpenGL 3.2 + WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB

OpenGL 3.2 では Profile の選択が出来るようになりました。
wglCreateContextAttribsARB() 呼び出し時の WGL_CONTEXT_PROFILE_MASK_ARB
で指定できる Profile は次の 2つ。

・WGL_CONTEXT_CORE_PROFILE_BIT_ARB
・WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB

Core profile はシェーダーを用いた新しい API セット、Compatibility profile は
互換性を持った API です。
テストで GeForce の 190.57 を使っている分には、どちらもほとんど差がありませんでした。

関連エントリ
OpenGL 3.2 GeometryShader をもう少し使ってみる
OpenGL GLSL のバージョン
OpenGL 3.2 の GeometryShader
OpenGL のはじめ方
OpenGL ES 2.0 Emulator
OpenGL ES 2.0
OpenGLES2.0 DDS テクスチャを読み込む
OpenGLES2.0 Direct3D とのフォーマット変換
OpenGLES 2.0 頂点フォーマットの管理
OpenGLES2.0 の頂点
OpenGLES2.0 D3D座標系
OpenGLES2.0 シェーダー管理