Monogame(XNA)で大量のタイルを効率的に描画する


8

明確な答えが見つからないため、この質問をします。

まず最初に、ゲームといくつかのことについていくつか述べておきます。ゲームは、シンプレックスノイズを使用して手続き的に生成された世界に設定されたRTSになる予定です。世界は64 x 64のスプライトで作られた16 x 16のサイズのチャンクで構成されています。チャンクを動的にロードおよびアンロードできましたが、これは問題なく動作します。世界はRimworldのように見えるので、さまざまなレイヤーのスプライト(最初に地形、トランジションスプライト、木、デカールなど)を上から下に見ていきます。新しく生成された世界には、環境に影響を与える可能性のあるエンティティ(たとえば、町になった村)が含まれている場合があり、それによってチャンクが含まれる場合があります。これはある種の関数を使用して計算できると確信していますが、注意する必要があります。

主な問題私は持っているが、私はズームアウト時に、より多くのタイルがひどく、性能に影響を与える描かれています。スプライトが約30000の場合、描画セクションにかかる時間は8 msで、60 FPSで実行するのに必要な時間の半分です。そして、それは単なる地形です。テクスチャアトラスを使用して描画カウントを制限しています(6カウントで描画される30000スプライト)。

目標は、国全体を見ることができることに、町/村/都市レベルからすべての方法をズームアウトすることができることです。これ動的に行う必要があります(たとえば、ミニマップアイコンをクリックするのではなく、Supreme Commanderのようにスクロールバックするだけです)。

私はこの問題に関する多くの記事を読みましたが、それが機能する場所の明確な例を見つけたり、見たりしていません。これが私が見つけたテクニックのリストであり、うまくいくはずです:

  • ここで説明するように汚れた長方形。ここでは新しいものだけを描画し、残りはバックバッファに保持します。これは非常に理にかなっていますが、Monogameでこれを実装する方法については無知です。
  • ここで説明するように、私が優先する選択は、RenderTargetを幅広く使用することです。ここでは、RenderTargetに描画し、それをテクスチャとして保存します。私の場合、64 x 64で構成される16 x 16チャンクは、1024 x 1024テクスチャを作成します。これがパフォーマンスの点で機能するかどうかは本当に疑わしいですが、結果は非常に詳細なテクスチャで構成され、ほとんどが静的(地形/木など)であり、それほど変化しないという事実を考えると使用に最適です。ただし、これは、チャンクに変更が加えられるたびに、SetDataを使用してTexture2Dを変更する必要があることも意味します。これは、私が経験したことから、CPUにかなりの負荷がかかります。ただし、テクスチャが16 x 16の場合、実際に機能する可能性があり、それ自体のメモリとディスク使用量も削減されます。
  • SpriteBatchでSourceRectangleを指定してテクスチャをティルします。巨大な草原/海では、1つのスプライトしか描画されないため、これはプラスです。ただし、さまざまな色とさまざまなスプライトが混在する詳細な地形(バイオームとバイオームの遷移)の場合、大きな違いは生じないと思います。
  • 詳細を損なう私の独自のソリューションは、標準の白い64 x 64タイルを使用して、周囲の4つのタイルの色を指定し、次に、前の4つのタイルを覆うように拡大縮小することでした。ここでの違い(無地のタイルを除く)は、風景が目に見えて変化したことです。また、森林は完全に正方形ではないため、個別に描く必要があることにも触れておきます。

誰かがこの問題に取り組む方法についてアイデアを持っている場合、たとえそれが特定のことを妥協することを意味するとしても(ディテール、または16 x 16スプライトの使用など)、それを聞いてみたいと思います。


ユーザーがそのくらいズームアウトさせてはいけません
バリント

また、手続き的に生成されたMonoGameプロジェクトでパフォーマンスの問題が発生しました。私の解決策は、レンダリングされるものを制限することでした(ズームアウト範囲を制限し、画面上にあるものだけを描画します)。MonoGameは「すべてのフレームをすべて描画する」マントラで設計されているようですが、MonoGameの経験が豊富な方に修正していただければ幸いです。
論理的誤り

どのようにタイルをレンダリングしますか?より具体的には-SpriteBatchを使用していますか、またどのように使用していますか?
loodakrawa 2017年

回答:


3

グラフィックカードは、逆の方向に描画するのではなく、何度も描画するように最適化されています。

あなたの場合、何度も描画したいものがたくさんあります(異なるテクスチャ)(タイルの数)。

IMOは、パフォーマンスを大幅に向上させるために実行できる最も簡単な最適化です。

  1. テクスチャをアトラスに結合します。これでグラフィックカードは多数の小さなテクスチャではなく、1つ(または2つ)のテクスチャのみを描画するようになるため、処理速度が大幅に向上します。
  2. SpriteSortMode.TextureモードでSpriteBatchを使用してタイルをバッチ処理します。これにより、スプライトがテクスチャでソートされ、テクスチャスイッチの数が実際の異なるテクスチャの数に最小化されます。このアプローチの欠点は、スプライトが深度でソートされないことです。タイルがすべて同じ深さであるため、これはおそらく問題ありません。私はあなたが他のものも持っていると思いますので、タイル用のSpriteBatchと他のものすべてのための別のものを分離することは理にかなっています(SpriteSortMode.BackToFrontに設定)。

両方を行うことをお勧めします。幸運を。


2

SpriteBatchはタイルの描画には適していないことがわかりました。1つの理由は、スプライトは動的オブジェクト用であり、複数の目的で使用されるためです。もう1つの理由は、信じられないほどCPUに依存していることです。説明で説明したように、30.000個のオブジェクトのコストは8ミリ秒です(私のチップセットは最大30%です)。また、GPUに新しい描画呼び出しを行う必要がある前に、最大3000のスプライトを描画できます(バッチおよびすべて)。

解決策は、テクスチャのクワッドを使用して自分のタイルを描画することでした。これをVertexBufferと組み合わせることで、1回の描画呼び出しで最大500.000のテクスチャタイルを描画できました。描画メソッドは約1/20 ミリ秒を消費しました。もちろん、私はおそらくそれほど多くを描く必要はないでしょうが、いずれにしても、最終的にGPUを安定した60 fpsで75%のワークロードにすることができました。


とてもいいですね。詳細について教えてください-チュートリアルに従っていますか、またはこのプロセスの詳細な説明へのリンクはありますか?
loodakrawa 2017

2
XNA / Monogameとテクスチャクワッドについて言及しているあらゆる場所について、そのほとんどを学びました。ただし、riemers.netは、機能するものを迅速に設定したいだけの人を対象としているので、非常に役立ちました。VertexBuffersとIndexBuffersについても説明しているので、彼の3Dテレインチュートリアルに特に従う必要があります。タイルタイプの地形に変更しても問題ありません。
Guguhl Pluhs

それがあなたの質問に最も答えるのであれば、あなたはそれを受け入れられたものとしてマークすることを歓迎します:)
Vaillancourt

@GuguhlPluhs私はこれが遅いことを知っていますが、あなたはまだこの主題についていくつかの情報を持っていますか?私も同じ問題に直面しています。
Jelle
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.