tuedaの日記

2011-11-04

[][] テクスチャー・バッファー・オブジェクト(TBO)とは何か

f:id:tueda_wolf:20111103193256p:image

OpenGLにわりと新しく入った機能でテクスチャー・バッファー・オブジェクト(TBO)というものがある。

これが非常にわかりにくい機能で過不足なく明快に説明した文章を見たことがない。

まずTBOはテクスチャーバッファーという名前のオブジェクトではなく、

2Dテクスチャーやキューブテクスチャーなどのテクスチャーの1種である。

つまりTBOオブジェクトではなくTBOテクスチャーである。

通常の2Dテクスチャーが2次元の画像データから値を拾ってくるのに対して、

TBOは汎用のバッファーオブジェクトから値を拾ってくるテクスチャーである。

Texture・2Dの連想でTexture・BufferObjectと名前を付けたのだろうが、激しく誤解を受けそうなネーミングである。

素直にBufferObject・Textureにしておけばいいのに。。。。

GLSLの中では通常の2Dテクスチャーはsampler2D型で表しtexture()関数でアクセスするが、

TBOはsamplerBuffer型で表しtexelFetch()関数でアクセスする。

通常テクスチャー座標は(0,1)の浮動小数点型だがTBOのアクセスは整数値の(0,サイズ)を使用する。

つまり普通のCの1次元配列と同じようにアクセスできる。

なおTBOは1次元しかない。

一つだけ注意点は(どこにも書いてないんだけど...)texelFetch()の戻り値は浮動小数点型のvec4だけであり、

内部フォーマットとして整数データ型を指定した場合は0〜1にマッピングされて帰ってくる。

つまりCPU型でRGBA8型を指定して(255,255,255,255)を入れた場合、texelFetch()の戻り値は(1.0,1.0,1.0,1.0)である。

整数値をそのまま取り出すフラグがあっても良さそうなものだがないようだ。

なおTBOテクスチャーはSamplerオブジェクトを必要としない(セットしても影響はない)。

使い方

まずglGenBuffer()を使って汎用バッファーを確保しglBufferData()でデータを転送する。

データを転送するにはどこかのバインディングポイントにバインドする必要があるので

GL_TEXTURE_BUFFERにバインドしているがこれはどこでもかまわない。

ちなみにここバッファーをバインドする命令はglBindBuffer()なので、

テクスチャーをバインドするglBindTexture()と間違えないように。

    glGenBuffers    (1, &gradient_buffer);
    glBindBuffer    (GL_TEXTURE_BUFFER, gradient_buffer);
    glBufferData    (GL_TEXTURE_BUFFER, 4*gradient_size, gradient_color, GL_STATIC_DRAW);

次にテクスチャーバッファーオブジェクト(・テクスチャー)を作成する。

個人的にはTBOはバッファー・テクスチャーに解明すべきだと思う。あくまでテクスチャーの名前なので。

    glGenTextures   (1, &gradient_texture);
    glActiveTexture (GL_TEXTURE0);
    glBindTexture   (GL_TEXTURE_BUFFER, gradient_texture);
    glTexBuffer     (GL_TEXTURE_BUFFER, GL_RGBA8, gradient_buffer);

GL_TEXTURE_BUFFERにバインドされたテクスチャー(gradient_texture)はデータの供給元として

汎用バッファーオブジェクトに関連付けられなければならない。それがglTexBuffer()である.

この関連付けは1回だけ行えばよく、次回からは通常のテクスチャーと同じようにバインドしなおすだけで使用できる。

2次元テクスチャーに画像データを供給する命令がglTexImage2D()だったことを考えると、

供給元がImage2DからBufferに変わっただけである。

これで準備完了。

GLSL側。samplerBuffer型の変数tex_gradientにtexelFetch()関数でアクセスる。

テクスチャー座標を整数値に直すにはテクスチャーサイズが必要だが、それはtextureSize()関数で取得できる。

いちいち自分でUniform変数で渡す必要がないのが便利ですね。

#version 330
uniform samplerBuffer tex_gradient;
in  vec2 tex_coord;
out vec4 frag_color;

void main ()
{
    int i = int(tex_coord.s * textureSize(tex_gradient));
    frag_color = texelFetch (tex_gradient, i);
}

なお通常の2Dテクスチャーはサンプラーをバインドしないと動作しないが、

TBOテクスチャーはサンプラーを必要としない。バインドなくても動作するし、してもなんの影響もなく動作する。

通りすがり通りすがり 2013/04/27 16:41 >texelFetch()の戻り値は浮動小数点型のvec4だけ
いやジェネリック型ですよ?例えば整数型にしたいならsamplerBufferの前に修飾子iをつけるといい(http://www.opengl.org/sdk/docs/manglsl/xhtml/texelFetch.xml)。
read/writeのできるimageBuffer(http://www.opengl.org/registry/specs/ARB/shader_image_load_store.txt)も参考までに。

uniform isamplerBuffer tex_gradient;

texelFetch (tex_gradient, i).r // 整数