6

別の XNA の質問の時間です。今回は純粋に技術的な設計の観点からです。

私の状況は次のとおりです。GPU 計算に基づいてパーティクル エンジンを作成しましたが、完全にはほど遠いですが、動作します。私の GPU は 10,000 個のパーティクルを苦労せずに簡単に処理できるので、さらに多くのパーティクルを追加できても驚かないでしょう。

私の問題:同時に多数のパーティクルを作成すると、フレーム レートが低下します。なんで?ほとんどメモリ操作のみを含むように最小限に抑えましたが、大量の CPU 使用率。

パーティクルの作成は、次のような CPU 呼び出しによって引き続き行われます。

  • メソッドはパーティクルを作成し、呼び出しを行います。
  • クワッドは頂点の形で作成され、バッファに格納されます
  • バッファが GPU に挿入され、CPU は他のことに集中できる

フレームごとに 1 つのパーティクルを作成する約 4 つのエミッターがある場合、FPS が低下します (確かに、1 秒あたり 4 フレームしかありませんが、15 個のエミッターでは FPS が 25 に低下します)。

パーティクルの作成:

        //### As you can see, not a lot of action here. ###
        ParticleVertex []tmpVertices = ParticleQuad.Vertices(Position,Velocity,this.TimeAlive);
        particleVertices[i] = tmpVertices[0];
        particleVertices[i + 1] = tmpVertices[1];
        particleVertices[i + 2] = tmpVertices[2];
        particleVertices[i + 3] = tmpVertices[3];
        particleVertices[i + 4] = tmpVertices[4];
        particleVertices[i + 5] = tmpVertices[5];

        particleVertexBuffer.SetData(particleVertices);

私の考えでは、パーティクルをそれほど頻繁に作成するべきではないかもしれません。GPU にすべてを作成させる方法があるかもしれません。または、これらの作業をどのように行うかを知らないだけかもしれません。;)

編集: パーティクルをそれほど頻繁に作成しない場合、見栄えを良くするための回避策は何ですか?

したがって、良いパーティクル エンジンをどのように設計する必要があるか、また、どこかで間違ったルートをたどった可能性があるかどうかを知っていただくために、ここに投稿しています。

4

1 に答える 1

4

GPU にすべてを作成させる方法はありません ( SM4.0 を必要とするジオメトリ シェーダーを使用しない限り)。

CPU の効率を最大化するためにパーティクル システムを作成する場合、次のように頂点とインデックス バッファに 100 個のパーティクルを事前に作成します(例として数を選択するだけです)。

  • 四角形を含む頂点バッファーを作成します (パーティクルごとに 4 つの頂点であり、6 つではありません)。
  • 「時間オフセット」値と「初期速度」値を格納できるカスタム頂点形式を使用します ( XNA Particle 3D サンプルと同様) 。
  • 各パーティクルの時間オフセットが最後のパーティクルよりも 1/100 小さくなるように時間値を設定します (したがって、オフセットはバッファを通じて 1.0 から 0.01 の範囲になります)。
  • 初速度をランダムに設定します。
  • 各パーティクルの 4 つの頂点を使用して、必要な 2 つの三角形を提供するインデックス バッファーを使用します。

そしてすばらしいことに、これを行う必要があるのは 1 回だけです。同じ頂点バッファーとインデックス バッファーをすべてのパーティクル システムに再利用できます (最大のパーティクル システムに対して十分な大きさがあれば)。

次に、次の入力を受け取る頂点シェーダーを作成します。

  • 頂点ごと:
    • 時間オフセット
    • 初期速度
  • シェーダー パラメータ:
    • 現在の時刻
    • パーティクルの寿命 (これは、パーティクル時間のラップアラウンド値でもあり、使用されているバッファ内のパーティクルの割合でもあります)
    • パーティクル システムの位置/回転/スケール (ワールド マトリックス)
    • 粒子サイズ、重力、風など、その他の興味深い入力
    • タイムスケール(リアルタイムを取得するため、速度やその他の物理計算が理にかなっています)

その頂点シェーダー (これもXNA Particle 3D Sampleのようなもの) は、パーティクルの頂点の位置を、その初期速度と、そのパーティクルがシミュレーション内にあった時間に基づいて決定できます。

各粒子の時間は次のようになります (疑似コード):

time = (currentTime + timeOffset) % particleLifetime;

つまり、時間が進むにつれて、パーティクルは一定の速度で放出されます (オフセットのため)。そして、パーティクルが で死ぬたびにtime = particleLifetime (または 1.0 でしょうか? 浮動小数点モジュラスが紛らわしいです)、時間がループして に戻りtime = 0.0、パーティクルがアニメーションに再び入ります。

次に、パーティクルを描画するときが来たら、バッファ、シェーダ、およびシェーダ パラメータを設定し、DrawIndexedPrimitives. さて、ここに巧妙なビットがあります。アニメーションの途中でパーティクルが開始されないように設定startIndexします。primitiveCountパーティクル システムが最初に起動したとき、1 つのパーティクル (2 つのプリミティブ) を描画し、そのパーティクルが消滅するまでに、100 個すべてのパーティクルを描画します。

しばらくすると、1 番目のパーティクルのタイマーがループして 101 番目のパーティクルになります。

(システムに 50 個のパーティクルのみが必要な場合は、パーティクルの有効期間を 0.5 に設定し、頂点/インデックス バッファー内の 100 個のパーティクルのうち最初の 50 個のみを描画します。)

パーティクル システムをオフにするときが来たら、同じことを逆に行いstartIndexますprimitiveCount

ここで、関連する数学と、パーティクルにクワッドを使用することについての詳細を説明したことを認めなければなりませんが、理解するのはそれほど難しくありません。理解すべき基本原則は、頂点/インデックス バッファーをパーティクルの循環バッファーとして扱っているということです。

循環バッファの欠点の 1 つは、パーティクルの放出を停止すると、現在の時間がパーティクルの寿命の倍数になったときに停止しない限り、アクティブなパーティクルのセットがバッファの端にまたがってギャップが生じることです。中間 - したがって、2 つの描画呼び出しが必要です (少し遅くなります)。これを回避するには、停止する直前まで待つことができます。ほとんどのシステムではこれで問題ありませんが、一部のシステムでは奇妙に見える場合があります (例: 即座に停止する必要がある「遅い」パーティクル システム)。

この方法のもう 1 つの欠点は、パーティクルを一定の速度で放出する必要があることです。ただし、これは通常、粒子システムではかなり一般的です (明らかに、これはシステムごとであり、速度は調整可能です)。少し調整すると、爆発効果 (すべてのパーティクルが一度に放出される) が可能になります。

つまり、可能であれば、既存の粒子ライブラリを使用する価値があるかもしれません。

于 2010-06-29T05:27:42.980 に答える