※OpenGLに関連する項目は 別ページ に整理しました。ここにあるものは内容が古い場合があります。
(サンプルは一番最後にあります)
OpenGLにはこれまで、プラットフォームに依存しないテクスチャへ直接レンダリングを行う手法は 存在していませんでしたが、framebuffer object が規定され、ウィンドウ以外への オフスクリーンレンダリングが可能になったことで、テクスチャへのレンダリングも プラットフォームに依存せず行えるようになりました。 ここでは framebuffer object の概要について記述します。
framebuffer object は、OpenGLでプラットフォームに依存せず オフスクリーンレンダリングを実現する機構です。 OpenGL 2.0 の正式仕様には取り込まれていません。 OpenGL拡張 EXT_framebuffer_object として規定されています。
従来のフレームバッファは、実際の表示ウィンドウと密接に関連付けられて おり、OSが提供していました。 フレームバッファは、
の組から成っており、これらは論理バッファ(logical buffer)と呼ばれます。
framebuffer object は、従来のフレームバッファと同等の論理バッファの組を、 表示ウィンドウと別個に保持し、それに対してレンダリングすることを可能とします。 複数のカラーバッファ、デプスバッファ、ステンシルバッファを扱うことが できます。アキュームレーションバッファは現時点では使用できないようです。
それぞれのバッファは、単純な2次元のピクセル列であり、texture もしくは framebuffer と共に新たに導入される renderbuffer と関連付けて 使用します。 すなわち、テクスチャへのレンダリング、もしくは、renderbuffer へのレンダリングが可能になる、ということです。 texture および renderbuffer は framebuffer-attachable と呼ばれます。 texture および renderbuffer に含まれる2次元ピクセルの配列を framebuffer-attachable image と呼びます。
注意すべき点として、framebuffer と renderbuffer は明確に区別しておく必要が あると思います。名前から受ける印象が似ているため私は混乱しました。 renderbuffer の実体は2次元ピクセル配列であり texture buffer と同じ 階層で取り扱われるバッファです。framebuffer は複数の論理バッファを統合する より抽象的なデータ構造であると考えればよいと思います。登場するバッファを 整理すると図のようになります。
下準備編
レンダリング編
利用編
次のようなサンプルを構築します。
マウスの左クリックで立方体の回転を、右クリックでティーポットの回転を それぞれOn/Offします。
本サンプルでは、カラーバッファに texture を、デプスバッファに renderbuffer を割り当てます。
まず、各バッファの識別子を格納するためのGLuint型の変数を宣言します:
GLuint texture_name; GLuint renderbuffer_name; GLuint framebuffer_name;
texture の初期化は以下の通りです。通常と異なる部分は、 texture に登録する画像が必要なく、バッファの確保のみを行えば よい点です。glTexImage2D の最後の引数に 0 を渡すことでバッファの確保のみを 行えるようです。OpenGL仕様内には記述を見つけることが できませんでしたがウェブ上のサンプルプログラムには散見されるのでよしとします:
void
InitTexture( void )
{
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glGenTextures( 1, &texture_name );
glBindTexture( GL_TEXTURE_2D, texture_name );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, TEXTURE_WIDTH, TEXTURE_HEIGHT,
0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
}
renderbuffer の扱いは texture とほとんど同じです。 renderbuffer の確保は glGenRenderbuffersEXT で行います。 使い方は glGenTextures と同じです。 生成した renderbuffer に対して設定を行いますが、 以後の設定の対象となる renderbuffer を glBindRenderbufferEXT で 指定します。これも texture の場合と同じです。 glRenderbufferStorageEXT でバッファの型とサイズを指定します。 グラフィクスRAMの上に2次元配列をmallocすることに相当します。 texture の場合にはパラメータの設定をいくつか行いますが、 物体に貼られることのない renderbuffer には必要ありません:
void
InitRenderbuffer( void )
{
glGenRenderbuffersEXT( 1, &renderbuffer_name );
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, renderbuffer_name );
glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
RENDERBUFFER_WIDTH, RENDERBUFFER_HEIGHT );
}
renderbuffer の型として、ここでは、デプスバッファとして 用いるために GL_DEPTH_COMPONENT を指定しています。 カラーバッファとして用いるならば GL_RGBA などを指定します。 指定できる型は、specification には color-renderable, depth-renderable, stencil-renderable と 書かれています。glDrawPixels などで使用できるフォーマットと 思っておけばよいでしょう。
カラーバッファおよびデプスバッファに割り当てるバッファを 確保したら、framebuffer object を生成します。
glGenFramebuffersEXT は texture などと同じ一般的なOpenGLの作法で framebuffer の識別子を確保します。続いて glBindFramebufferEXT で 以下の設定を適用するバッファを指定するのも同じです。
framebuffer 内の各論理バッファに、どの texture や renderbuffer を割り当てるかを指定する関数が、 glFramebuffer*EXT です。* の部分に割り当て元のバッファの 種類が入ります。texture を割り当てるなら glFramebufferTexture2DEXT を、 renderbuffer を割り当てるなら glFramebufferRenderbufferEXT を使います。 3次元 texture の割り当ても行えます。その場合には割り当てる平面の z 座標を指定します。 割り当て先は、第2引数で指定します。色バッファなら GL_COLOR_ATTACHMENT?_EXT を、デプスバッファなら GL_DEPTH_ATTACHMENT を 指定します。色バッファは複数割り当てることができるのでGLSLと組み合わせると multi render target を実現できます。 割り当てが終わったら、無用のバグの発生を防止するために、 デフォルトのフレームバッファに戻しておくのがよいと思います。 glBindFramebufferEXT の引数に 0 を入れて呼び出します:
void
InitFramebuffer( void )
{
glGenFramebuffersEXT( 1, &framebuffer_name );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_name );
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, texture_name, 0 );
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_RENDERBUFFER_EXT, renderbuffer_name );
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
}
さて、実際の framebuffer object へのレンダリングを行うわけですが、 難しい点は何もありません:
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_name );
で先程設定を行った framebuffer へと書き込み先を変更します。
その後の処理は通常のレンダリングと全く変わりありません。 glut で double buffering を用いている場合は glutSwapBuffers() を 最後に実行しますが、オフスクリーンレンダリングの場合は glFlush() を 実行します。ただし、glBindFramebufferEXT でレンダリング先を 切り替えると glFlush と同等の処理がなされるようですので趣味の範疇です。
OpenGL の様々なコンテキストはフレームバッファ間で共通です。 viewport や各行列の設定、更にはライティングなどの設定も共有されています。 デフォルトのフレームバッファの視野に関する設定を、 以下のような関数を作成して保存しておきます。デフォルトの フレームバッファも含めてクラス化するのが筋だと思います:
GLuint viewport[ 4 ];
void
SaveFramebufferStatus( void )
{
glGetIntegerv( GL_VIEWPORT, viewport );
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
}
void
RestoreFramebufferStatus( void )
{
glViewport( viewport[ 0 ], viewport[ 1 ], viewport[ 2 ], viewport[ 3 ] );
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
}
void
RenderToTexture( void )
{
/* switch to framebuffer object */
SaveFramebufferStatus();
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, framebuffer_name );
:
/* execute drawing */
glFlush();
/* switch to default buffer */
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
RestoreFramebufferStatus();
}
サンプルプログラムを以下に置いておきます。GLEWを導入しておく必要があります。
http://chihara.naist.jp/people/STAFF/imura/computer/OpenGL/fbotest.lzh
framebuffer object が正しく設定されている(complete)ことを確認するには、 glCheckFramebufferStatusExt を使います:
if ( glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT )
!= GL_FRAMEBUFFER_COMPLETE_EXT ) {
cerr << "framebuffer is not complete" << endl;
}
framebuffer object に対して、ステンシルバッファの指定を行っていませんが、 特に問題は起こっていません。本サンプルではステンシルバッファを 利用していないからかもしれません。 特に指定しなければデフォルトのフレームバッファと共有するのでしょうか。 デフォルトのフレームバッファと framebuffer object とで サイズが異なる場合に、バッファのリサイズ(再確保)による 速度低下が起こるのではないかと懸念していますが確かめていません。
Astle, D: More OpenGL Game Programming