私はC++/openGLで静的な3Dブロックの世界のようなMinecraftを書いています。私はフレームレートの改善に取り組んでおり、これまで八分木を使用して錐台カリングを実装してきました。これは役に立ちますが、フレームレートが中程度から悪い状態が続いています。次のステップは、より近い立方体によって視点から隠されている立方体をカリングすることです。しかし、私はこれを達成する方法について多くのリソースを見つけることができませんでした。
5 に答える
Zバッファ(または「デプスバッファ」)を有効にしてレンダーターゲットを作成します。次に、不透明なオブジェクトをすべて並べ替えて、前面から背面にレンダリングされるようにします。つまり、最初にカメラに最も近いオブジェクトを並べ替えます。アルファブレンディングを使用するものはすべて、不透明なオブジェクトをすべてレンダリングした後でも、後ろから前にレンダリングする必要があります。
もう1つの手法は、オクルージョンカリングです。ジオメトリを安価に「ドライレンダリング」してから、深度テストに失敗したピクセルの数を調べることができます。DirectXとOpenGLにはオクルージョンクエリのサポートがありますが、すべてのGPUでサポートできるわけではありません。
欠点は、レンダリングと結果のフェッチの間に遅延が必要になることです。セットアップによっては(述語タイリングを使用する場合など)、フレーム全体になる場合があります。つまり、オブジェクト自体よりも大きいバウンディングボックスをレンダリングしたり、カメラカット後に結果を却下したりするなど、そこでクリエイティブである必要があります。
そしてもう1つ:より伝統的な解決策(オクルージョンカリングと同時に使用できる)は、「ポータル」を介して接続された「部屋」として領域を定義する部屋/ポータルシステムです。現在の部屋からポータルが表示されていない場合、ポータルに接続されている部屋は表示されません。それでも、ビューポートをクリックしてポータルから見えるものに移動できます。
このMinecraftレベルのレンダラーで採用したアプローチは、基本的に錐台が制限されたフラッドフィルです。16x16x128チャンクは16x16x16チャンクレットに分割され、それぞれに関連するジオメトリのVBOがあります。プレーヤーの場所にあるチャンクレットグリッドでフラッドフィルを開始して、レンダリングするチャンクレットを見つけます。塗りつぶしは次の制限があります。
- ビュー錐台
- ソリッドチャンクレット-チャンクレットの側面全体が不透明なブロックである場合、フラッドフィルはその方向にチャンクレットに入りません
- 方向-フラッドは方向を逆にしません。たとえば、現在のチャンクレットが開始チャンクレットの北にある場合は、南のチャンクレットにフラッディングしないでください。
うまくいくようです。私はAndroidを使用しているので、より複雑な分析(Mike Danielsが指摘したアンチポータル)はより多くのジオメトリをカリングしますが、私はすでにCPUに制限されているため、あまり意味がありません。
アランに対するあなたの答えを見たばかりです。カリングはあなたの問題ではありません。遅いのは、OpenGLに送信する内容と方法です。
描画するもの:ブロックごとに立方体をレンダリングせず、不透明なブロックに隣接する透明なブロックの面をレンダリングします。たとえば、石のブロックの3x3x3の立方体について考えてみます。プレーヤーが中央のブロックを見る方法がないため、中央のブロックを描画しても意味がありません。同様に、プレイヤーは2つの隣接する石のブロックの間の顔を見ることはないので、それらを描画しないでください。
描画方法:Alanが指摘したように、VBOを使用してジオメトリをバッチ処理します。あなたは彼らが物事をどれほど速くするか信じられないでしょう。
既存のコードへの変更を最小限に抑えたより簡単なアプローチは、ディスプレイリストを使用することです。これはMinecraftが使用するものです。
レンダリングするブロックの数とハードウェアは何ですか?最新のハードウェアは非常に高速であり、ジオメトリに圧倒されることは非常に困難です(ハンドヘルドプラットフォームについて話している場合を除く)。適度に最近のデスクトップハードウェアでは、凝ったカリングのトリックなしで、フレームあたり数十万の立方体を毎秒60フレームでレンダリングできるはずです。
個別の描画呼び出し(glDrawElements / Arrays、glBegin / glEndなど)で各ブロックを描画している場合(ボーナスポイント:glBegin / glEndを使用しないでください)、それがボトルネックになります。これは初心者にとってよくある落とし穴です。これを行う場合は、テクスチャとシェーディングのパラメータを共有するすべての三角形をまとめて、セットアップごとに1回の呼び出しにする必要があります。ジオメトリが静的で、フレームごとに変更されない場合は、三角形のバッチごとに1つの頂点バッファオブジェクトを使用する必要があります。
通常、一度にビュー錐台にゲーム世界全体のごく一部しか存在しない場合は、これを八分木による錐台カリングと組み合わせることができます。頂点バッファは引き続き静的にロードされ、変更されません。錐台は八分木をカリングして、錐台内の三角形のインデックスバッファーのみを生成し、フレームごとに動的にアップロードします。
カメラの近くにサーフェスがある場合は、見えない領域を表す錐台を作成し、その錐台に完全に含まれているオブジェクトをカリングできます。下の図でC
は、はカメラで|
あり、はカメラの近くの平らな面であり、で構成される錐台形の領域.
は閉塞領域を表しています。表面はアンチポータルと呼ばれます。
.
..
...
....
|....
|....
|....
|....
C |....
|....
|....
|....
....
...
..
.
(もちろん、他の回答やコメントで述べられているように、深度テストと深度書き込みもオンにする必要があります。OpenGLで行うのは非常に簡単です。)
Zバッファを使用すると、ポリゴンが正しくオーバーラップすることが保証されます。
深度テストを有効にすると、すべての描画操作で、ピクセルを画面に配置する前にZバッファがチェックされます。
凸状のオブジェクトがある場合は、(パフォーマンスのために)背面カリングを有効にする必要があります!
コード例:
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
GL_FRONTまたはGL_BACKを渡すglCullFace()の動作を変更できます...
glCullFace(...);
//「ゲームの世界」を描きます...