TOP

ABOUT

PROGRAM

DIALY







Occlusion Culling

 1.はじめに…
 Occlusion Culling(遮蔽カリング)をやってみました.




 2.Occlusion Cullingとは…
 Occlusion Queryを使って,物体を描画するのにどれくらいピクセルが割り当てられているかを調べ,ピクセルが全く割り当てられていない場合,カリングをして物体を描画しないようにしてしまう. …というのが自分なりの理解です.
今回,Occlusion Cullingを実装するのに,Occlusion Queryというものを使用します.これは,OpenGL 1.5から使えるようになった機能だったと思うので,まずOpenGL 1.5以上に対応している必要があります.まぁ,最近のGPUとかを積んでいるなら問題ないじゃないかと思います.
それで肝心なのは使い方です.
まずは,glGenQuery()関数を使って,クエリのIDみたいなものを生成しておきます.下のような感じです.
00068:  GLuint g_teapotQuery = 0;
00069:  GLuint g_planeQuery = 0;
00070:  GLuint g_cubeQuery = 0;

00166:  	// クエリを生成
00167:  	glGenQueries( 1, &g_teapotQuery );
00168:  	glGenQueries( 1, &g_planeQuery );
00169:  	glGenQueries( 1, &g_cubeQuery );
 IDみたいなのを発行したら,あとは描画したい処理の前後にglBeginQuery()関数とglEndQuery()関数を入れてはさみます.そのあとで,glGetQueryObjectuiv()などの関数を使って,ピクセル数(フラグメント数)を取得します.
 プログラムを作る時に描画処理の前後に挟んで,1回描画して,そのあとにピクセル数を基に描画するかどうかという感じにしていたのですが,どうも一回だけの描画でクエリを作ると,奥から順番に描画されている場合は問題がないのですが,順番がめちゃくちゃだと結果がおかしくなります.そんなわけで,「ソートしてから描画すりゃいいんじゃねぇの?」とも思ったわけですが,いちいちソートするのは面倒です.で,どうすりゃいいか?って言ったら,単純に1回描画のところを2回描画してやれば深度判定したあとに描画することになるので,ソートする必要はなくなります.そんなわけで,2回描画します.1回目は深度値を埋めるため,2回目はクエリを生成するためです.ネットによくあるサンプルなどもよ〜く見ると2回描画してあります.よくわかっていないころは「2回も描画して無駄じゃん!」なんて思っていたのですが,別に無駄に書いているわけではなかったですね.
で肝心のコードなんですが,下のような感じになります.
00071:  GLuint g_teapotPixelCount = 0;
00072:  GLuint g_planePixelCount = 0;
00073:  GLuint g_cubePixelCount = 0;


00278:  //--------------------------------------------------------------------------------------------------
00279:  // Name : Render3D()
00280:  // Desc : 3次元シーンの描画
00281:  //--------------------------------------------------------------------------------------------------
00282:  void Render3D()
00283:  {
00284:  	// バッファをクリア
00285:  	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
00286:  
00287:  	////////////////////////////////
00288:  	// 深度値を初期化
00289:  	////////////////////////////////
00290:  
00291:  	// キューブ
00292:  	glPushMatrix();
00293:  	glTranslatef( 0.0f, 0.0f, 4.0f );
00294:  	glColor4f( 0.0f, 0.0, 1.0f, 1.0f );
00295:  	glutSolidCube( 3.0 );
00296:  	glPopMatrix();
00297:  
00298:  	// やかん
00299:  	glPushMatrix();
00300:  	glTranslatef( 0.0f, 0.0f, 1.5f );
00301:  	glColor4f( 0.0f, 1.0, 0.0f, 1.0f );
00302:  	glutSolidTeapot( 1.0f );
00303:  	glPopMatrix();
00304:  	
00305:  	// 平面
00306:  	glPushMatrix();
00307:  	glTranslatef( 0.0f, 0.0f, 0.0f );
00308:  	glScalef( 1.0f, 1.0f, 0.01f );
00309:  	glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
00310:  	glutSolidCube( 5.0 );
00311:  	glPopMatrix();
00312:  
00313:  	////////////////////////////////
00314:  	// クエリを生成
00315:  	////////////////////////////////
00316:  
00317:  	// キューブ
00318:  	glPushMatrix();
00319:  	glBeginQuery( GL_SAMPLES_PASSED, g_cubeQuery );
00320:  	glTranslatef( 0.0f, 0.0f, 4.0f );
00321:  	glColor4f( 0.0f, 0.0, 1.0f, 1.0f );
00322:  	glutSolidCube( 3.0 );
00323:  	glEndQuery( GL_SAMPLES_PASSED );
00324:  	glPopMatrix();
00325:  
00326:  	// やかん
00327:  	glPushMatrix();
00328:  	glBeginQuery( GL_SAMPLES_PASSED, g_teapotQuery );
00329:  	glTranslatef( 0.0f, 0.0f, 1.5f );
00330:  	glColor4f( 0.0f, 1.0, 0.0f, 1.0f );
00331:  	glutSolidTeapot( 1.0f );
00332:  	glEndQuery( GL_SAMPLES_PASSED );
00333:  	glPopMatrix();
00334:  	
00335:  	// 平面
00336:  	glPushMatrix();
00337:  	glBeginQuery( GL_SAMPLES_PASSED, g_planeQuery );
00338:  	glTranslatef( 0.0f, 0.0f, 0.0f );
00339:  	glScalef( 1.0f, 1.0f, 0.01f );
00340:  	glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
00341:  	glutSolidCube( 5.0 );
00342:  	glEndQuery( GL_SAMPLES_PASSED );
00343:  	glPopMatrix();
00344:  
00345:  	// 結果を取得して描画かどうか決める
00346:  	glGetQueryObjectuiv( g_planeQuery, GL_QUERY_RESULT, &g_planePixelCount );
00347:  	glGetQueryObjectuiv( g_teapotQuery, GL_QUERY_RESULT, &g_teapotPixelCount );
00348:  	glGetQueryObjectuiv( g_cubeQuery, GL_QUERY_RESULT, &g_cubePixelCount );
00349:  
00350:  	// バッファをクリアしておく
00351:  	glClear( GL_COLOR_BUFFER_BIT );
00352:  
00353:  	////////////////////////////////////
00354:  	// Occlusion Culling
00355:  	////////////////////////////////////
00356:  
00357:  	// カリングが分かるように深度テストOFF
00358:  	glDisable( GL_DEPTH_TEST );
00359:  	
00360:  	// キューブ
00361:  	if ( g_cubePixelCount > 0 )
00362:  	{
00363:  		glPushMatrix();
00364:  		glTranslatef( 0.0f, 0.0f, 4.0 );
00365:  		glColor4f( 0.0f, 0.0f, 1.0f, 0.5f );
00366:  		glutSolidCube( 3.0 );
00367:  		glPopMatrix();
00368:  	}
00369:  
00370:  	// やかん
00371:  	if( g_teapotPixelCount > 0 )
00372:  	{
00373:  		glPushMatrix();
00374:  		glTranslatef( 0.0f, 0.0f, 1.5f );
00375:  		glColor4f( 0.0f, 1.0, 0.0f, 0.75f );
00376:  		glutSolidTeapot( 1.0f );
00377:  		glPopMatrix();
00378:  	}	
00379:  
00380:  	// 平面
00381:  	if ( g_planePixelCount > 0 )
00382:  	{
00383:  		glPushMatrix();
00384:  		glTranslatef( 0.0f, 0.0f, 0.0f );
00385:  		glScalef( 1.0f, 1.0f, 0.01f );
00386:  		glColor4f( 1.0f, 0.0f, 0.0f, 0.1f );
00387:  		glutSolidCube( 5.0 );
00388:  		glPopMatrix();
00389:  	}
00390:  
00391:  	// 深度テストON
00392:  	glEnable( GL_DEPTH_TEST );
00393:  }
00394:  
こんな感じでOcclusion Cullingができます.DirectXを使っている方はDirectX 10のサンプルにOcclusion Predicate Queryを使うやつ(DrawPredicated)があるのでそちらを,参考にされた方がいいと思います.
あとは,問い合わせによってハードウェアをストールさせることもあったりするそうで,GPU Gems1と2にそのあたりの話が書いてあった気がするので,そちらも一読されておいたほうがいいと思います.


 Download
本ソースコードおよびプログラムを使用したことによる如何なる損害も製作者は責任を負いません。
本ソースコードおよびプログラムは自己責任でご使用ください。
プログラムの作成にはMicrosoft Visual Studio 2008 SP1 Professionalを用いています。