OpenGLでたくさんのタイルを描く、最新の方法


35

私は小さなタイル/スプライトベースのPCゲームをチームで作業していますが、パフォーマンスの問題に直面しています。前回OpenGLを使用したのは2004年頃でしたので、コアプロファイルの使用方法を自分で教えてきましたが、少し混乱しているように感じます。

フレームごとに250〜750の48x48タイルと、約50のスプライトを画面に描画する必要があります。タイルは新しいレベルがロードされたときにのみ変化し、スプライトは常に変化しています。タイルの一部は4つの24x24ピースで構成されており、ほとんどの(ただしすべてではない)スプライトはタイルと同じサイズです。多くのタイルとスプライトはアルファブレンディングを使用します。

現在、私はこれらすべてを即時モードで行っていますが、これは悪い考えです。それでも同じように、チームメンバーの1人がそれを実行しようとすると、非常に悪いフレームレート(〜20-30 fps)を取得します。さらに多くのタイルがある場合、特にこれらのタイルの多くが切り分けられます。これはすべて、私が問題が行われている描画呼び出しの数であると思うようになります。

私はこれに対するいくつかの可能な解決策を考えましたが、私は愚かなことに時間を無駄にしないように、彼らが話していることを知っている一部の人々によってそれらを実行したかったです:

タイル:

  1. レベルがロードされたら、すべてのタイルを一度大きなホーンテクスチャにアタッチされたフレームバッファに描画し、各フレーム上にそのテクスチャを含む大きな長方形を描画します。
  2. レベルがロードされたら、すべてのタイルを静的な頂点バッファーに入れて、そのように描画します。glDrawElementsを1回呼び出すだけで、異なるテクスチャのオブジェクトを描画する方法があるのか​​、それとも私がやりたいことなのかわかりません。たぶん、すべてのタイルを大きな巨大なテクスチャに配置し、VBOで面白いテクスチャ座標を使用しますか?

スプライト:

  1. glDrawElementsを個別に呼び出して、各スプライトを描画します。これには多くのテクスチャ切り替えが関係しているようですが、これは悪いと言われています。ここでテクスチャ配列は便利でしょうか?
  2. 動的VBOを何らかの方法で使用します。上記の番号2と同じテクスチャの質問。
  3. ポイントスプライト?これはおそらくばかげています。

これらのアイデアのいずれかが理にかなっていますか?私が見渡せるどこかに良い実装はありますか?


タイルが移動したり変化したりせず、レベル全体で同じように見える場合は、最初のアイデア-フレームバッファーを使用する必要があります。最も効率的です。
-zacharmarz

テクスチャアトラスを使用して、テクスチャを切り替える必要はなく、他のすべてを同じにしてください。今、彼らのフレームレートはどうですか?
user253751 16

回答:


25

タイルをレンダリングする最速の方法は、頂点データをインデックス付きの静的VBOにパックすることです(glDrawElementsが示すように)。別のイメージに書き込むことはまったく不要であり、必要なメモリは非常に多くなります。テクスチャの切り替えは非常にコストがかかるため、おそらくすべてのタイルをいわゆるテクスチャアトラスにパックし、VBOの各三角形に適切なテクスチャ座標を与える必要があります。これに基づいて、ご使用のハードウェアに応じて、1000、さらには100000タイルをレンダリングすることは問題になりません。

タイルレンダリングとスプライトレンダリングの唯一の違いは、スプライトが動的であることです。そのため、最高でありながら簡単に達成できるパフォーマンスを得るには、スプライト頂点の座標を各フレームのストリーム描画VBOに入れ、glDrawElementsで描画するだけです。また、すべてのテクスチャをテクスチャアトラスにパックします。スプライトがめったに移動しない場合は、ダイナミックVBOを作成して、スプライトが移動したときに更新することもできますが、ここでは一部のスプライトのみをレンダリングするため、これは完全にやり過ぎです。

:あなたは、私はOpenGLとC ++で作られた小さなプロトタイプを見ることができ微粒子

私は、通常のマシン(Quad Core @ 2.66GHz)で平均400 fpsの約10000ポイントのスプライトをレンダリングします。CPUが制限されているため、グラフィックカードがさらにレンダリングできる可能性があります。ここではテクスチャアトラスを使用していないことに注意してください。パーティクルのテクスチャは1つしかないためです。パーティクルはGL_POINTSでレンダリングされ、シェーダーは実際のクワッドサイズを計算しますが、クワッドレンダラーもあると思います。

ああ、はい、正方形があり、テクスチャマッピングにシェーダーを使用しない限り、GL_POINTSは非常にばかげています。;)


スプライトは位置と使用するテクスチャを変更し、ほとんどのフレームでこれを行います。また、スプライトと非常に頻繁に作成および破壊されます。ストリーム描画VBOが処理できるのはこれらのことですか?
ニック

2
ストリーム描画とは、基本的に「このデータをグラフィックカードに送信し、描画後に破棄する」という意味です。したがって、フレームごとにデータを再度送信する必要があります。つまり、レンダリングするスプライトの数、その位置、テクスチャ座標、または色は関係ありません。ただし、すべてのデータを一度に送信し、GPUで処理するのはもちろん、即時モードよりもはるかに高速です。
マルコ

これはすべて理にかなっています。このためにインデックスバッファを使用する価値はありますか?繰り返される頂点は、すべての長方形の2つの角だけですよね?(私の理解では、インデックスはglDrawElementsとglDrawArraysの違いであるということです。それは正しいですか?)
Nic

1
インデックスなしではGL_TRIANGLESを使用できません。GL_TRIANGLESは、この描画方法が最高のパフォーマンスを保証する方法であるため、通常は不適切です。また、GL_QUADS実装はOpenGL 3.0で非推奨になりました(ソース:stackoverflow.com/questions/6644099/…)。三角形は、グラフィックカードのネイティブメッシュです。したがって、2 * 6バイトをさらに「使用」して、2つの頂点シェーダーの実行と、vertex_size * 2バイトを節約します。したがって、一般的には常に優れていると言えます。
マルコ

2
Particulateへのリンクは終了しています...新しいものを提供してくださいませんか?
SWdV

4

即時モードは遅いかもしれないが、そうではありません-ドローのこの数は呼び出してさえしてパフォーマンスの低下のようなものを見てすべきではないことを遅い(参考のために、でも愛する古い地震が陥ることなく、フレームごとに数千の即時モードのコールを管理することができますひどくダウン)。

ここでもっと面白いことが起こっていると思います。最初にやらなければならないことは、プログラムのプロファイリングに時間をかけることです。GLInterceptのような基本的なことでも実行して、時間の経過を確認してください。その結果に基づいて、主なボトルネックが何であるかについての実際の情報で問題に取り組むことができます。


いくつかのプロファイリングを行いましたが、パフォーマンスの問題は開発と同じマシンでは発生しないため、厄介です。問題はタイルの数とともに間違いなく増加し、タイルは文字通り描画される以外何もしないため、問題が他の場所にあることには少し懐疑的です。
ニック

では状態の変化はどうですか?不透明なタイルを状態ごとにグループ化していますか?
マキシマスミニマス

それは可能性です。これは間違いなく私の側でもっと注目に値します。
ニック

2

さて、私の最後の回答はここで手に負えなかったので、新しいものがあります。


2Dパフォーマンスについて

まず、いくつかの一般的なアドバイス:2Dは現在のハードウェアを必要としません。大部分が最適化されていないコードでも機能します。ただし、中間モードを使用する必要があるという意味ではありません。少なくとも、不必要なときに状態を変更しないようにしてください(たとえば、同じテクスチャが既にバインドされているときに新しいテクスチャをglBindTextureにバインドしない、CPUのifチェックがトンglBindTexture呼び出しよりも速く)、glVertexのようにまったく間違って馬鹿げたものを使用しない(glDrawArraysでさえはるかに高速であり、使用するのはこれ以上難しくありませんが、非常に「モダン」ではありません)。これら2つの非常に単純なルールを使用すると、フレーム時間は少なくとも10ミリ秒(100 fps)になります。さらに高速化するために、次の論理ステップはバッチ処理です。たとえば、多くの描画呼び出しを1つに束ねます。これには、テクスチャアトラスの実装を検討する必要があります。そのため、テクスチャバインドの量を最小限に抑えることができるため、1回の呼び出しで描画できる長方形の量を増やすことができます。約2ミリ秒(500 fps)になっていない場合は、何か間違ったことをしています:)


タイルマップ

タイルマップの描画コードを実装すると、柔軟性と速度のバランスを見つけることができます。静的VBOを使用できますが、アニメーションタイルでは機能しません。または、フレームごとに頂点データを生成し、上記で説明したルールを適用することもできます。これは非常に柔軟ですが、それほど速くはありません。

以前の回答では、フラグメントシェーダーがテクスチャリング全体を処理する別のモデルを紹介しましたが、依存するテクスチャルックアップが必要であるため、他の方法ほど高速ではないことが指摘されました。(基本的には、タイルインデックスのみをアップロードし、フラグメントシェーダーでテクスチャ座標を計算するという考え方です。つまり、1つの長方形だけでマップ全体を描画できます)


スプライト

スプライトには多くの柔軟性が必要であるため、「2Dパフォーマンスについて」で説明したものを除き、最適化が非常に困難です。また、画面に1万個のスプライトが同時に必要な場合を除き、おそらく努力する価値はありません。


1
そして、たとえ1万個のスプライトがあるとしても、現代のハードウェアはそれをまともな速度で実行すべきです:)
Marco

@ API-ビーストは何を待つ?フラグメントシェーダーでテクスチャUVをどのように計算しますか?フラグメントシェーダーにUVを送信することになっていますか?
HgMerk

0

すべて失敗した場合...

フリップフロップの描画方法を設定します。一度に他のすべてのスプライトのみを更新します。ただし、VisualBasic6と単純なビットブリットメソッドを使用しても、フレームごとに数千のスプライトをアクティブに描画できます。おそらく、スプライトを描画する直接的な方法が失敗しているように見えるため、これらの方法を調べる必要があります。(「レンダリング方法」を使用しているように聞こえますが、「ゲーム方法」のように使用しようとしています。レンダリングとは、スピードではなく明瞭さに関するものです。)

画面全体を何度も何度も再描画している可能性があります。変更された領域のみを再描画する代わりに。それはたくさんのオーバーヘッドです。コンセプトはシンプルですが、理解するのは簡単ではありません。

バージンの静的背景にバッファを使用します。画面にスプライトがない場合を除き、これ自体はレンダリングされません。これは、スプライトが描画された場所を「元に戻す」ために常に使用され、次の呼び出しでスプライトの描画を解除します。また、画面ではない「描画」するバッファも必要です。そこに描いてから、一度描いたら、それを一度画面にひっくり返します。これは、すべてのスプライトごとに1つのスクリーンコールになるはずです。(画面上に各スプライトを一度に1つずつ描画したり、一度にすべてのスプライトを実行しようとすると、アルファブレンディングが失敗します。)メモリへの書き込みは高速で、画面時間を「描画「。各ドローコールは、再びドローを試みる前に、リターン信号を待ちます。(v-syncではなく、実際のハードウェアティックであり、RAMの待機時間よりもかなり遅いです。)

これは、この問題が1台のコンピューターでのみ表示される理由の一部だと思います。または、すべてのカードがサポートしていないALPHA-BLENDのソフトウェアレンダリングにフォールバックしています。その機能を使用する前に、その機能がハードウェアでサポートされているかどうかを確認しますか?フォールバック(非アルファブレンドモード)がありますか?明らかに、ゲームコンテンツを低下させると思われるので、制限するコード(ブレンドするものの数)はありません。(これらがすべてアルファブレンドされた単なるパーティクルエフェクトである場合とは異なり、ハードウェアをサポートしている場合でもほとんどのシステムに大きな負担がかかるため、プログラマーがそれらを制限する理由です。)

最後に、アルファブレンドを必要なものだけに制限することをお勧めします。すべてが必要な場合...ユーザーにハードウェア要件の改善を要求するか、ゲームを望ましいパフォーマンスに低下させる以外に選択肢はありません。


-1

他の2Dゲームのように、オブジェクト用のスプライトシートと地形用のタイルセットを作成します。テクスチャを切り替える必要はありません。

三角形のペアごとに独自のテクスチャ座標が必要なため、タイルのレンダリングは面倒です。この問題には解決策がありますが、これはインスタンス化されたレンダリングと呼ばれます

たとえば、草のタイルとその位置のリストを持つことができるようにデータを並べ替えることができる限り、1回の描画呼び出しですべての草のタイルをレンダリングできます。必要なのは配列を提供することだけですモデルから各タイルのワールド行列へ。この方法でデータを並べ替えることは、最も単純なシーングラフであっても問題になりません。


-1:インスタンス化は、Beast氏の純粋なシェーダーソリューションよりも悪い考えです。インスタンス化は、中程度の複雑さ(約100個の三角形など)のオブジェクトをレンダリングするときのパフォーマンスに最適です。テクスチャ座標を必要とする各三角形タイルは問題ではありません。タイルマップを形成するために発生する、ゆるい四角形の束でメッシュを作成するだけです。
ニコルボーラス

1
@NicolBolas大丈夫、私は学習のために答えを残すつもりです
-dreta

1
ニコル・ボーラス、わかりやすくするために、これらすべてに対処する方法についてのあなたの提案は何ですか?マルコのストリームドローシング?これの実装を見ることができる場所はありますか?
ニック

@Nic:オブジェクトをバッファリングするストリーミングは、特に複雑なコードではありません。しかし、実際には、あなたが50の悪党について話しているだけなら、それは何もありません。パフォーマンスの問題を引き起こしているのは地形の描画である可能性が高いので、そのために静的バッファーに切り替えるだけで十分でしょう。
ニコルボーラス

実際、インスタンス化が必要と思われるように機能した場合、それは最良のソリューションになりますが、機能しないため、すべてのインスタンスを単一の静的vboにベイク処理する方法があります。
ジャリコンパ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.