7

私は通常SpriteBatch、2D ゲームの XNA/Monogame で作業しており、最近では などの 3D 描画方法を詳しく調べていDrawUserIndexedPrimativesます。私は、アニメーターがスプライトとテクスチャをせん断する機能を持ちたいというプロジェクトに取り組んでいます。

を使用すると、オブジェクトの剪断を開始するためにSpriteBatchマトリックスを渡すことができます。SpriteBatch何かのようなもの:

//translate object to origin
Matrix translate1 = Matrix.CreateTranslation(-rectangle.X, -rectangle.Y, 0);

//skew the sprite 33 degrees on the X and Y axis
Matrix skew = Matrix.Identity;
skew.M12 = (float)Math.Tan(33 * 0.0174532925f);
skew.M21 = (float)Math.Tan(33 * 0.0174532925f);

//translate object back
Matrix translate2 = Matrix.CreateTranslation(rectangle.X, rectangle.Y, 0);
Matrix transform = translate1 * skew * translate2;

_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied,
                    SamplerState.PointWrap, DepthStencilState.Default,
                    RasterizerState.CullCounterClockwise, null, transform);
_spriteBatch.Draw(_texture, rectangle, Color.White);
_spriteBatch.End();

SpriteBatchこれの明らかな欠点は、せん断されたスプライトごとに新しい開始呼び出しと終了呼び出しを行う必要があることです。SpriteBatch現在、ゲームを開始するために必要な呼び出しは 2 つだけです。1 つは UI 用、もう 1 つはワールド用です。私たちのアーティストは、木を揺らしたり、生き物の足や手足をアニメートしたりするためにせん断を使用したいと考えていたので、オプションを与えると、その数が 10 以上の個別のバッチにジャンプすることがわかりました。

平均的なレベルには、それぞれ 10 ~ 20 のスプライトを含む約 250 の要素があります。

1000 個のスプライトで描画を呼び出す Android 用のテストを作成しました。1000 回、600 回、約 11 秒または約 53 fps でスキューなしですべてを描画できます。しかし、スプライトを 10 個ごとに歪ませる (100 個の新しいSpriteBatch呼び出しを追加する) と、47 秒、つまり約 12fps かかります。

それは本当に悪いです。わずか 200 個のスプライト (10 個ごとに歪んだもの) の場合でも、テストは 28fps に低下します。

そのため、 で描画された Quads を使用して同じテストも作成しましたDrawUserIndexedPrimitives。各クワッドはBasicEffect、Game クラスで作成され、Spriteクラス コンストラクターを介して渡される共有を使用します。pass.Apply()次のように、それぞれの前に World Matrix と Texture を設定します。

if (_basicEffect != null)
{
     foreach (EffectPass pass in _basicEffect.CurrentTechnique.Passes)
     {
        _basicEffect.World = Transform;
        _basicEffect.Texture = _texture;
        pass.Apply();

        GraphicsDevice.DrawUserIndexedPrimitives
            <VertexPositionNormalTexture>(
            PrimitiveType.TriangleList,
            _quad.Vertices, 0, 4,
            _quad.Indices, 0, 2);
}

1000 個のスプライトの場合、スキューなしで、これにより 12 fps が得られます (1000 回のspriteBatch呼び出しに似ていると思います)。それは本当に悪いです。しかし、10 番目のスプライトごとに歪んだ 200 のスプライトのみで、46 fps を取得しSpriteBatchますDrawUserIndexedPrimitives

- -私の質問 - -

DrawUserIndexedPrimitivesを継承する独自のクラスに含まれるスプライトを保持しながら、(または同様のもの)への呼び出しをバッチ処理するにはどうすればよいDrawableGameComponentですか? この最後の部分は、ゲーム エンジンの性質と、アニメーションやコリジョンなどを処理する方法のために非常に重要です。

Vertex Buffers と についてできる限り読んだことDrawIndexedPrimitivesがありますが、頭がまとまらず、この方法で描画されたスプライトに新しいテクスチャとワールド変換を割り当てる方法がわかりません。

SpriteBatchこれらの呼び出しをバッチ処理した場合と同等またはより優れたパフォーマンスが期待できますか?

4

1 に答える 1

2

ここにはいくつかのオプションがあるようです。私は主にPC上のXNA 4.0に精通しているため、これらのすべてが可能/パフォーマンスであるとは限らないことに注意してください。

簡単でハックな方法

スプライトを描画するときにカラー チャネルを使用していないようです。この手法は、例が実際のコードを表していることを前提としています。

スプライトの色付けにスプライト カラーが必要ない場合は、スプライトごとのデータをカスタムの頂点/ピクセル シェーダーに渡す方法として、スプライト カラーをハイジャックできます。たとえば、次のようにします。

var shearX = MathHelper.ToRadians(33) / MathHelper.TwoPi;
var shearY = MathHelper.ToRadians(33) / MathHelper.TwoPi;
var color = new Color(shearX, shearY, 0f, 0f);
_spriteBatch.Draw(_texture, rectangle, color);

2 * piこれは、それぞれ赤と緑のカラー チャネルに格納されている係数として、x と y のせん断値を表します。

次に、これらの値を取得してその場でせん断計算を実行するカスタム頂点シェーダーを作成できます。その方法については、 Shawn Hargreaves の記事を参照してください

ハイブリッドアプローチ

DrawUserIndexedPrimitivesもう 1 つの比較的単純な可能性は、従来のスプライト バッチ処理をコード と組み合わせることです。

良好なパフォーマンスの鍵は、状態の変化を最小限に抑えることです。そのため、スプライトを慎重に並べ替えることが大いに役立ちます。を使用して 1 回のパスですべての歪んでいないスプライトを描画できるようにスプライトを編成し、実際に必要なスプライトSpriteBatchのみを低速の手法で描画します。DrawUserIndexedPrimitivesこれにより、特定のフレーム内のほとんどのスプライトが歪んでいないと仮定すると、GPU に送信されるバッチの数が大幅に削減されます。

バッチ処理 + カスタム頂点フォーマット

これはおそらく最良の手法ですが、ほとんどのコードを記述する必要があります。そのどれもが特に複雑というわけではありません。

内部で機能する方法SpriteBatchは、動的な頂点バッファーを維持することです。このバッファーは CPU に取り込まれ、1 回の呼び出しですべて描画されます。Shawn Hargreaves が、この種の処理がどのように行われるかについて、概要を説明しています

この手法を使用するように拡張する際の問題DrawUserIndexedPrimitivesは、厄介なワールド マトリックスです。シェーダーには、特定のワールド マトリックスを特定のスプライトにアタッチする適切な方法が実際にはありません (プラットフォームがサポートしていないと思われるハードウェア インスタンス化を使用している場合を除きます)。それで、あなたは何ができますか?

カスタムの頂点フォーマットを作成すると、最初の手法のように、各頂点にせん断値をアタッチし、それらを使用して頂点シェーダーでせん断を実行できます。これにより、ゲームのすべてのスプライトを 1 回の呼び出しで描画できるようになり、非常に高速になります。

カスタム頂点宣言に関する情報は、こちらで確認できます。

于 2013-01-25T20:30:52.657 に答える