2Dグラフィック-スプライトシートを使用する理由


62

スプライトシートからスプライトをレンダリングする方法の例を見てきましたが、2Dゲームでスプライトを処理する最も一般的な方法である理由を理解していません。

特定のスプライトタイプの各アニメーションフレームを独自のテクスチャとして扱うことで作成したいくつかのデモアプリケーションで、2Dスプライトレンダリングを開始しました。このテクスチャのコレクションは辞書に保存されています。これは私にとってはうまくいくようで、アニメーションをgif / mngファイルとして作成し、フレームを個々のpngに抽出する傾向があるため、ワークフローにかなり適しています。

個々のテクスチャからではなく、単一のシートからレンダリングすることに顕著なパフォーマンス上の利点はありますか?1秒間に数百万のポリゴンを画面に描画できる最新のハードウェアでは、数十ダースの50x100px長方形を処理する2Dゲームでも問題になりますか?

テクスチャをグラフィックメモリにロードしてXNAで表示する実装の詳細は、かなり抽象化されているようです。私が知っているのは、ロードされたときにテクスチャがグラフィックデバイスにバインドされ、ゲームループ中にテクスチャがバッチでレンダリングされることです。ですから、私の選択がパフォーマンスに影響を与えるかどうかは明確ではありません。

私は、ほとんどの2Dゲーム開発者がそれらを使用しているように見えるいくつかの非常に良い理由があるのではないかと疑っていますが、その理由はわかりません。


いいえ、50の奇妙なスプライトにとってそれほど重要ではありません。しかし、失うものは何ですか?
共産主義のダック

回答:


69

スプライトシートを使用するための強力な議論は、グラフィックカードで使用可能なテクスチャの数を制限できることです。したがって、グラフィックライブラリは常にテクスチャを削除し、GPUでテクスチャを再割り当てする必要があります。大きなテクスチャを一度割り当てるだけの方がはるかに効率的です。

また、テクスチャサイズは通常2の累乗であると考えてください。したがって、50x100pxのスプライトがある場合、サイズ64x128pxのテクスチャ、または最悪の場合は128x128pxのテクスチャを割り当てます。それはグラフィックメモリを浪費しているだけです。すべてのスプライトを1024x1024pxテクスチャにパックすると、20x10のスプライトが許可され、水平および垂直に24ピクセルしか失われません。テクスチャを可能な限り効率的に使用するために、サイズの異なるスプライトでさえ1つの巨大なスプライトシートに結合される場合があります。

補遺:スプライトシートを使用する非常に重要な理由は、GPUの描画呼び出しの量を減らすことです。これは、パフォーマンスに顕著な影響を与える可能性があります。これは他の回答で述べられていますが、完全性のためにこれを追加して、これによりいくつかの可視性が得られるようにします。


1
おかげで、私はそれらの点のいずれも考慮していませんでした-使用可能なテクスチャの数の制限が何であるかおおよそ知っていますか?グラフィックの仕様で言及されているこの統計を見たことはありません-制限は基本的にグラフィックRAMの量と同じですか?
コロンボ

通常、制限はGPUメモリです。古いカードのテクスチャの数にはいくつかの制限があると思っていましたが、その点を証明するリファレンスが見つからないようです。
-bummzack

12
量に制限がなくても、たくさんの小さな画像を移動するためのバストラフィックは、1つ(または少数)の大きな転送ほど効率的ではありません。
モルダーチャイ

5
良い点ですが、最も重要な点は、描画呼び出しのカウントを下げてパイプラインのストールを最小限に抑えることです。GPUは、大量の小さなジョブよりも、少量の大きなジョブを好みます。すべてのスプライトを小さな量の大きなシートに入れると、一度にテキストステージを設定して、一度にたくさんのスプライトを描画できます。Spritebatchがこれを行います。複数のスプライトシートがある場合は、スプライトバッチにテクスチャで並べ替えるように指示してください
Cubed2D

追加テクスチャ圧縮は、そのすべての上に、オーバーヘッドの非常に小さな小さな量にたるみスペースを回すだろう
ジョーPlante

36

使用する引数は、1回の描画呼び出しで複数のものをレンダリングする機能だと思います。たとえば、フォントレンダリングを考えてみましょう。スプライトシートを使用しない場合、各文字を個別のテクスチャスワップとそれに続く描画呼び出しでレンダリングする必要があります。それらをスプライトシートに投げると、1回の描画呼び出しで文全体をレンダリングできます(コーナーに異なるUVを指定するだけで、フォントの異なる文字が選択されます)。これは、多くのプラットフォームでのレンダリングの非常に現実的なオーバーヘッドが、あまりにも多くのAPI呼び出しを行う場合のレンダリングのはるかに効率的な方法です。

スペースを節約することもできますが、そもそもスプライトシートへのパッキングの内容によって異なります。


2
描画呼び出し時に+1タッチ。(受け入れられた答えには見られなかったもの)
マイケルコールマン

11

各描画呼び出しには、ある程度のオーバーヘッドがあります。スプライトシートを使用すると、アニメーションの同じフレームを使用していないもの(より一般的には同じマテリアル上にあるものすべて)の描画をバッチ処理して、パフォーマンスを大幅に向上させることができます。ゲームによっては、これは最近のPCにとってはそれほど重要ではないかもしれませんが、たとえばiPhoneでは間違いなく重要です。


2
それは間違いなく重要です。グラフィックハードウェアは、ほとんどのゲームがその方向を向いているため、少数の詳細なモデル(キャラクターなど)に大量のポリゴンをプッシュするように最適化されています。その結果、数ダースの描画呼び出しのそれぞれで、できるだけ多くのポリゴンをプッシュする必要があります。都市のような大きなシーンをレンダリングするゲームエンジンは、描画呼び出しを減らすために、複数のテクスチャをオンザフライで組み合わせることがよくあります。
ジョッキング

多くの3Dハードウェアでのテクスチャu、vマッピング、およびビットブリッティング操作がスプライトシートの恩恵を受けるかどうかは間違いなく不思議です。
ジョープランテ

7

パフォーマンスの考慮事項に加えて、アートを作成するときにスプライトシートが便利です。各キャラクターは独自のシートに入れることができ、スクロールするだけですべてのフレームを見ることができます。すべてのフレームで一貫した外観を保つのに役立ちます。

さらに、AS3でコーディングし、各画像ファイルには個別の埋め込み命令が必要です。別のリソースクラスを使用している場合でも、すべてのフレームに24個の埋め込みを埋め込むのではなく、スプライトシート全体の埋め込みを1つにしたいと思います。


1
+1。OPはFlashを使用していませんが、埋め込みまたは読み込みもスプライトシートを優先するポイントです。また、(各「フレーム」に対して)数百のリクエストよりも1つのリクエストが優先されるWebサーバーからアセットがロードされる場合にも関係します。
bummzack

間違いなく、オンラインゲームのサーバー要求を減らしたいと考えています。画像を.swfまたはスプライトシートに入れる方がどちらがこの主な目標を達成するのかを検討しました(最終的に、スプライトシートはアーティストにとって労働集約的ではないと判断しました)。
ジョッキング

7

XNAでスプライトシートを使用するもう1つの理由は、Xbox360開発では、プロジェクト内にあるファイルの数に関連する展開/読み込み時間が遅いという既知の問題があることです。そのため、多数の小さな画像ファイルを1つの画像ファイルに結合すると、その問題に対処するのに役立ちます。


5

そのような十分な答えがあるので、ここでメモリ/ GPUについて言及するつもりはありません。

代わりに、作業中に、そして作業後にアートをクリアできます。歩行サイクルを見るために10個の画像をフリックする代わりに、すべてのフレームを一度に見ることができます。これを配布したい場合、処理するファイルは10個ではなく1個だけです。

また、(組織化の観点から)character.pngとenemy.pngをcharacterwalk01からcharacterwalk10に、さらにcharacterattack01からcharacterattack05にすると、さらにクリーンになります。


4

グラフィックスメモリには、ディスク上のファイルシステムのような肥大化したメモリ管理がありません。反対の場合:シンプルかつ高速に保たれます。

このようなシンプルで高速なメモリ管理の1つは、幅と高さを半分にすることで複数の小さなスプライトに切り取られる単一の巨大な4096x4096スプライトを持つようなものです。(同様の1次元メモリ管理手法が存在しますが、名前を忘れました。)最終的には、最適化を実行する必要があります。

実行時にそのようなメモリ管理をスキップするために、開発者は、コンパイル時に、またはグラフィックの設計プロセス中に、単一の大きなスプライトを複数の小さなスプライトで自動的に満たします。


3

また、初期化中にあなたを救うかもしれないI / O操作を忘れないでください。CDドライブから読み込む場合、各ファイルは余分なI / O呼び出しであり、シークに時間がかかる場合があります(現代のHDDはファイルごとに約15ミリ秒、CDは50〜100ミリ秒ですか?)。これにより、取得する必要があるファイルは1つだけなので、初期化にかかる時間を大幅に節約できます。また、アプリケーションが他の処理(GPUの待機など)を行っている場合に、OSがこれらのI / O操作をキャッシュできるようにします。


1
一方、ファイルシークの問題を解決したい場合は、カスタム形式を使用して、すべてのスプライトを1つのファイルにパックすることができます。ほとんどのゲームはそれを行います。
ティグロー

0

パフォーマンスがより効率的である以外に、私はそれが単純に簡単だと感じています。アートを作成するときに画像が常に境界内に収まるようにするには、少し余分な作業が必要ですが、現在処理しているすべての画像を表示できる方がはるかに簡単です。

私の意見では、コーディングも簡単です。私は単純に次のようなオブジェクトの配列を持っています:

sheet = {
    img: (the actual image),
    rects: [
        {x:0, y:0, w:32, h:32},
        {x:32, y:0, w:32, h:32},
        {x:64, y:0, w:32, h:32}
    ]
};

sheets.push(sheet);

次に、各アイテムに対して、sheetとrectの2つの値を指定します。シートはオブジェクトの配列のインデックスになり、rectはそのシートのrects配列のインデックスになります。

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