OpenGLで頂点を描画する最も効率的な方法


8

私はOpenGL 3Dゲームを書いています。地形や使用中のオブジェクト用の多数の三角形があります。

私はOpenGL公式ガイドから勉強していて、最初に提示された方法は、描画したい各頂点のglVertex後に関数を呼び出すglBeginことです。

ただし、何千もの三角形を描く必要がある場合、この方法はかなり古くて非効率的です。各フレームで変化しない情報をグラフィックカードのメモリに保存する方法があるので、各フレームをレンダリングすると、その情報はすでにそこにあります。より「不安定な」情報については、おそらくフレームごとに新しい頂点データを送信する必要があるため、別の手法の方が効率的かもしれません。

私の質問に来ます:頂点情報をハードウェアに送信し、レンダリングするための指示を与えるために使用される最も近代的で効率的なOpenGL関数と技術の明確な説明をお願いします。

フレーム中にめったに変化しない頂点データをレンダリングするための順序を送信するための最良の方法とOpenGLツールは何ですか(私は地形とオブジェクトについて考えます)、フレーム中に大きく変化する頂点データを送信するための最良の方法は何ですか?


そうです、glBegin()とglEnd()はイミディエイトモードレンダリングの一部であり、かなり古風です。今日では、たとえばVBOを使用します。正確にどのように使用するかは、シナリオによって異なります。
タラドール2013年

私もOpenGLを学習しようとしている学生です。頂点をより速く描画するには、頂点バッファーオブジェクト(VBO)を使用する必要があることを知っています。その方法を説明するのに十分な知識はありませんが、優れた(無料のオンライン)書籍へのリンクを提供できます。私はそれをすくい取り、小さな三角形を作りましたが、それは本当に複雑です。Arcsynthesis Modern OpenGLチュートリアル私は今、LazyFooのチュートリアルを行っています。それらは、glBegin()、glEnd()固定関数パイプラインから始まり、固定関数パイプラインを基盤として使用して、より新しいOpenGLに移行します。私はjをします
ベンボット2013年

回答:


19

大まかに言って、答えは「状況次第」です。パーティクルの更新を送信する方法は、一連のGPUスキンモデルを送信する方法とはかなり異なります。

頂点/インデックスバッファー

ただし、一般的な意味では、最近のすべてはVBO(頂点バッファーオブジェクト)で行われます。古い即時モードAPI(glBegin/ glEnd)は、ドライバーの内部頂点バッファーシステムのファットラッパーとしておそらく実装されています。

静的オブジェクトの場合は、静的VBOを作成して、頂点データを入力します。いずれかの頂点が共有されている場合(通常はその場合)、おそらくインデックスバッファーも作成する必要があります。これにより、メッシュの変更(転送帯域幅を節約できる可能性がある)と処理(頂点シェーディング時間を節約できる)のために同じデータを複数回送信する必要性が減少します。インデックス付きの描画とインデックスなしの描画を行うときは、異なる関数で描画することに注意してください。

動的オブジェクトの場合は、バッファの動的セットを使用して同じことを行います。

上級ノート

地形のような大きなピースの場合、メッシュを複数のピースに分割することはおそらくないでしょう。特に20万の三角形が表示されているときにGPUが1億の三角形をレンダリングするようにすることは、特にそれらが並べ替えられておらず、オーバードローや無駄なフラグメントシェーダーの呼び出しが多い場合、非常に無駄です。メッシュを大きなチャンクに分割し、視錐台内にあるものだけをレンダリングします。錐台にあるかもしれないが、完全に丘や建物の後ろなどにあるチャンクを取り除くために使用できる、より高度なカリングテクニックもあります。ドローコールのカウントダウンを維持することは良いことですが、ドローコールの最小化と非表示のジオメトリの描画の最小化の間には、バランスを取る必要があります(特定のアプリ/ハードウェアについて見つける必要があります)。

GPUバッファーについて覚えておくべき重要なことの1つは、GPUがバッファーを読み取っている間は、そこに書き込むことができないことです。ドライバーに、バッファーの古いコピーを破棄しても(それが終わったら)いいことを知らせ、新しいものを与える必要があります(古いものがビジー状態の場合)。もちろん、OpenGLがこれを実行するための関数がなかったのは長い間ありました(現在、GL 4.3のInvalidateBufferDataと、拡張機能としていくつかの古い実装があります)。むしろ、ほとんどのドライバが実装する非標準的で一般的な動作があります。更新する前にバッファを破棄するには、次のようにします。

glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);

もちろん変更GL_ARRAY_BUFFERしてGL_DYNAMIC_DRAW、バッファの適切な値にます。静的バッファーは更新されない(または更新されるべきではない)ため、そのようなバッファーの破棄について心配する必要はほとんどありません。

を使用する方が速い場合glBufferDataglBufferSubData、を使用すると高速になる場合があることに注意してくださいglMapBuffer。それは本当にドライバーとハードウェアに依存します。現世代のPCハードウェアはおそらく最速ですがglBufferData、確実にテストしてください。

別の手法は、インスタンス化を使用することです。インスタンス化により、頂点/インデックスバッファーにデータの複数のコピーを描画する単一の描画呼び出しを行うことができます。たとえば、100個の同一の岩がある場合、100個の独立したドローを作成するのではなく、それらをすべて一度に描画することをお勧めします。

インスタンス化するときは、インスタンスごとのデータを別のバッファーに配置する必要があります(各個人のオブジェクトの位置など)。これは、均一バッファー(D3D用語では定数バッファー)、テクスチャバッファー、またはインスタンスごとの頂点属性のいずれかです。繰り返しますが、どちらが速いかは異なります。インスタンスごとの属性はおそらくより速く、間違いなく使いやすいですが、多くの一般的なGL実装はまだサポートしていませんglBindingAttribDivisor、使用できるかどうか、そして本当に速いかどうかを確認する必要があります(一部の古いドライバーは、追加によってインスタンス化をエミュレートしましたバッファを使用すると、独立した描画呼び出しを行うよりもインスタンス化を使用する方が遅くなり、標準的な方法はありません... OpenGLを使用する喜び)。

頂点キャッシュ最適化のアルゴリズムもあります。これは、バッファ内の頂点/インデックスを順序付けして、最新のGPUの頂点キャッシュで意志を再生する動作です。GPUは頂点のシェーダーのみを実行してから、頂点キャッシュにキャッシュしますが、他の頂点のためのスペースを確保するには、早めに排除する必要がある場合があります。(たとえば、2つの三角形はどちらも頂点を共有していますが、それらの間に100個の三角形が描画されています。共有された頂点は、おそらく頂点シェーダーによって2回無駄に評価されることになります。)

これらの機能のいくつかは、GLまたはGLESの新しい十分なバージョンを必要とします。たとえば、GLES2はインスタンス化をサポートしていませんでした。

常にプロファイル

繰り返しになりますが、パフォーマンスを重視する場合は、可能な各方法をテストし、ターゲットハードウェアでのアプリの方が速い方法を確認してください。異なるメーカーの異なるハードウェア/ドライバーが異なるだけでなく、ハードウェアのクラス全体が本質的に異なる場合があります。典型的なモバイルGPUは、典型的な個別のデスクトップGPUとは非常に異なる獣です。「最高」のテクニックは、必ずしも別のベストではありません。

パフォーマンスに関しては、常に懐疑的です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.