Miyabiarts.net

一年に一度の更新頻度

FBO

今回はFBO(Frame Buffer Object)を扱います。
FBOが何かと一言で言ってしまえば、ディスプレイではなくテクスチャ上にレンダリング結果を書き出すためのものです。
ハードウェアの拡張機能の一つに入るため、グラフィックボードによっては使えない場合がありますが、最近の環境ではまず間違いなく動くでしょうから、それほど心配する必要はないはずです。

今回、サンプルとして一度FBOにテクスチャ付きのキューブをレンダリングし、そのレンダリング結果をテクスチャとして再度キューブに貼りつけたものをディスプレイ上に表示します。

FBOはFBOハンドルに対して、RGBテクスチャおよび深度テクスチャを関連付けることによって有効にします。

テクスチャの作成

各テクスチャの作り方は以下のとおりです。

RGBテクスチャ

RGBテクスチャですが、こちらは前回と全く同じように作成して大丈夫です。
ここで、TexSizeはテクスチャのサイズを示す定数で、今回はテクスチャは正方形になるように作成しています。

GL.GenTextures(1, out colorTex);
GL.BindTexture(TextureTarget.Texture2D, colorTex);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, TexSize, TexSize, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

深度テクスチャ

続いて、深度テクスチャですが、作り方の流れはRGBテクスチャと全く一緒ですが、GL.TexImage2Dに指定するパラメータが多少違います。

GL.GenTextures(1, out depthTex);
GL.BindTexture(TextureTarget.Texture2D, depthTex);
GL.TexImage2D(TextureTarget.Texture2D, 0, (PixelInternalFormat)All.DepthComponent32, TexSize, TexSize, 0, OpenTK.Graphics.OpenGL.PixelFormat.DepthComponent, PixelType.UnsignedInt, IntPtr.Zero);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

FBOの作成

FBOハンドルはGL.Ext.GetFramebuffersを用いて、他のハンドラと同じように作成できます。
そして、GL.Ext.BindFramebufferに作成したハンドラを設定し、操作対象とします。
続いて、先ほど作成したRGBテクスチャ・深度テクスチャをFBOに関連付けます。
以上で、FBOの準備は整いました。
ここで気をつけることは、GL.Ext.BindFramebufferで指定したFBOをそれ以降のレンダリング対象となるため、初期化の後にFBOを0と設定することで、ディスプレイをレンダリング対象としておきましょう。
これによって、何も表示されないということを防ぐことができます。

GL.Ext.GenFramebuffers(1, out fbo);
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fbo);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.ColorAttachment0Ext, TextureTarget.Texture2D, colorTex, 0);
GL.Ext.FramebufferTexture2D(FramebufferTarget.FramebufferExt, FramebufferAttachment.DepthAttachmentExt, TextureTarget.Texture2D, depthTex, 0);
GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);

レンダリング

GL.Ext.BindFramebufferでレンダリング対象とするFBOを指定することで、描画先を切り替えることができます。
今回は、まず作成したFBOに対して、テクスチャ付きのキューブをレンダリングします。
用いたシェーダは前回と全く同じものです。

GL.Ext.BindFramebufferでFBOを指定した後、気をつける点としては、クリアする領域をFBOに関連付けたテクスチャのサイズとするため、GL.Viewportを正しく設定してやることです。
その他の点に関して、通常のレンダリングと何も変わりません。

GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, fbo);
GL.Viewport(0, 0, TexSize, TexSize);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

...

GL.UseProgram(program);

GL.UniformMatrix4(GL.GetUniformLocation(program, "viewProjection"), false, ref viewProjectionMatrix);
GL.UniformMatrix4(GL.GetUniformLocation(program, "world"), false, ref worldMatrix);
			
GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, tex);
GL.Uniform1(GL.GetUniformLocation(program, "tex"), 0);
			
cube.Render();

今度は、FBOにレンダリングした結果をテクスチャとして貼りつけたキューブをディスプレイ上に表示します。
ディスプレイ上に表示するため、GL.Ext.BindFramebufferに0を与えます。
そして、今度はクリアする領域をディスプレイ(コントロール)のサイズとするようにGL.Viewportを正しく設定します。
最後に、FBOに関連付けていたテクスチャハンドラを貼り付けるように設定し、キューブを表示します。

GL.Ext.BindFramebuffer(FramebufferTarget.FramebufferExt, 0);
GL.Viewport(0, 0, glControl1.Width, glControl1.Height);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

...

GL.UniformMatrix4(GL.GetUniformLocation(program, "viewProjection"), false, ref viewProjectionMatrix);
GL.UniformMatrix4(GL.GetUniformLocation(program, "world"), false, ref worldMatrix);

GL.ActiveTexture(TextureUnit.Texture0);
GL.BindTexture(TextureTarget.Texture2D, colorTex);
GL.Uniform1(GL.GetUniformLocation(program, "tex"), 0);

cube.Render();

後片付け

後片付けでは、各ハンドラを削除します。

GL.DeleteTexture(tex);
GL.DeleteTexture(colorTex);
GL.DeleteTexture(tex);

GL.DeleteFramebuffers(1, ref fbo);

まとめ

以上が、OpenTKでFBOを扱うサンプルコードとなります。
今回のサンプルでは実際に何に使うかはイマイチ分かりづらいですが、環境マップやシャドウマップなどの応用は様々です。
そのうち、このあたりの応用も扱いたいと思います。

広告

Comments are closed.

%d人のブロガーが「いいね」をつけました。