スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Occlusion Query

2008年12月25日 00:29

リアルタイム 3D において Occlusion Culling は長年の課題でした。
実際に描画されるかどうかは Depth Test や Alpha Test が行われた後にしかわからないわけですから、Depth Buffer を参照する術を持たない(あっても物凄く非効率だった)Shader 技術が普及する以前の GPU では現実的な話ではありませんでした。

もっとも、頂点シェーダーが重い!というのは最近ではあまり考えられなくなりましたが、それでも描画がされないのならモーフィング処理などを行わなくても良くなるので、効果がないわけではありません。
さらに、最近の GPU は unified shader に対応しているのが当たり前になってるので、頂点シェーダーを通さなければそれだけ Pixel Shader に時間を費やせます。
また、オブジェクト描画によるフラグメントの更新が行われないのがわかるのなら、そのオブジェクトが使用する Texture を転送しなくてもいいワケなので、バス幅の節約にもなります。

さて、こんな Occlusion Culling、フラグメントまでいったかどうかの判定ですむわけですから、最近のハードウェアなら簡単に実装できそうな気もします。

実は、OpenGL では 1.5 (2003年7月にリリース)から採用されている機能で、DirectX には 10 から Occlusion Predicate Query という名前で実装されています。
二つとも、Bounding Sphere や Axis Aligned Bounding Box、Oriented Bounding Box などの簡易オブジェクトによる本描画前の遮断問い合わせを実現するための機能です。
従来は難しかったフラグメント単位での問い合わせが出来る点でなかなかに強力です。
OpenGL では GLSL の本格導入前(GLSL の本格導入は 2.0 から)から実装されているのが面白いですね。


そんな Occlusion Culling、GL での実装は簡単です。

1:まずオブジェクト ID を生成する(別になくても OK)
2:glBeginQuerry で遮断問い合わせ開始を宣言
3:実際に簡易的なオブジェクトの描画を行う
4:glEndQuerry で遮断問い合わせ完了を宣言
5:Depth Test をパスしたサンプル数を取得する
6:生成したオブジェクト ID を削除する。

です。


ちょっとここで注意。
windows で OpenGL をプログラムする場合、以下の点に注意しないといけません。

OpenGL を管轄する khronos グループでは OpenGL SDK という名前の SDK は配布していません!
OpenGL 1.1 までなら Windows Platform SDK に付属している OpenGL32.dll で使用できます。
  
OpenGL 1.2 以上 (1.5 や 2.0 など)の機能は Windows 付属のライブラリ(OpenGL32.dll)では扱えません。
使う場合は、OpenGL の HP から glext.h を落としてきてインクルードし、wglGetProcAddress 関数などで取得します。
  
 例:
  glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)wglGetProcAddress( "glGenFramebuffersEXT" );

また、glew というライブラリを使うことで、wglGetProcAddress を使用しなくても、いきなり glGenFramebuffersEXT(...) という感じで意識せずに使うことができます(オススメ)



wglGetProcAddress、glewInit などを使用する場合、必ず一つはコンテキストがないと失敗します。
コンテキストは wglCreateContext 関数で作成できます。



特に 1.2 以降にも対応している最新の OpenGL32.dll を探そうとするとハマるので注意してください。。
そんな gl.h や OpenGL32.dll は存在しません。
1.2 以降の関数には wglGetProcAddress や glewInit を通じてのみ、アクセス可能です。



閑話休題。
では、遮断問い合わせ処理に必要な関数をそれぞれ上げてみましょう。


void glGenQueries( GLsizei n, GLuint* ids )

ids に n 個の問い合わせオブジェクトの識別 ID を返します。




void glBeginQuery( GLenum target, GLuint id )

クエリー用の描画開始を宣言します。
target には GL_SAMPLES_PASSED だけを指定できます。
id には glGenQueries で生成した ID を渡してください。
フラグメント処理が通ったかどうかの判定だけなのですが、レンダーステートは反映されます。
ですので、レンダリング時間を増やすようなモードは無効にしたほうがいいでしょう。
(例えば Stencil 処理など)




void glEndQuery( GLenum target )

クエリー用の描画終了を宣言します。
target には同じく GL_SAMPLES_PASSED だけ指定可能です。




void glGetQueryObjectiv( GLuint id, GLenum pname, GLint* params )
void glGetQueryObjectuiv( GLuint id, GLenum pname, GLint* params )

二つとも、まったく同じ内容の関数です。
指定したクエリー id のフラグメント処理が行われたかどうかを問い合わせます。
id には glGenQueries で生成し、glBeginQuery で描画が行われた ID を渡してください。
pname には GL_QUERRY_RESULT、GL_QUERY_RESULT_AVAILABLE が指定できます。
GL_QUERRY_RESULT の場合は、params にはパスしたフラグメントかサンプル(MRT が有効な場合)数が帰ります。
これで params が 0 だった場合は、オブジェクトが全て遮断されていることになります。
GL_QUERY_RESULT_AVAILABLE を指定した場合、params には GL_TRUE か GL_FALSE が入ります。
GL_TRUE の場合は、渡した id での問い合わせが可能なことを意味し、GL_FALSE の場合は不可能なことを意味します。




void glDeleteQueries( GLsizei n, const GLuint* ids )

指定した n 個の ids を削除します。
ids には glGenQueries で生成した ID を渡してください。




以上の関数を使うことで occlusion culling が出来ます。
今回は GL の説明しかしませんでしたが、DirectX の場合も同じような機能ですので、そちらを参考にして下さい。

スポンサーサイト


コメント

    コメントの投稿

    (コメント編集・削除に必要)
    (管理者にだけ表示を許可する)

    トラックバック

    この記事のトラックバックURL
    http://angra.blog31.fc2.com/tb.php/130-0bf2f27d
    この記事へのトラックバック


    最近の記事


    上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。