ParticlesGS サンプル

このサンプルでは、Direct3D 10 のシェーダー、ストリーム出力、および DrawAuto を使用して、GPU 上で完全なパーティクル システムを実装します。

Bb205329.d3d10_sample_particlesgs(ja-jp,VS.85).jpg

Path

ソース : (SDK ルート)\Samples\C++\Direct3D10\ParticlesGS
実行可能ファイル : (SDK ルート)\Samples\C++\Direct3D10\Bin\x86 or x64\ParticlesGS.exe

サンプルが動作するしくみ

パーティクル システムの計算は、従来、GPU を使用して CPU 上で実行されており、視覚効果を得るために、パーティクルをポイント スプライトとしてレンダリングしていました。ジオメトリ シェーダーを使用すると、任意の量のデータをストリームに出力できるため、GPU で新しいジオメトリを作成したり、既存のジオメトリの計算結果を格納することが可能になります。

このサンプルでは、ジオメトリ シェーダーのストリーム出力機能を使用して、パーティクルの計算結果をバッファーに格納します。また、ジオメトリ シェーダーで新しいジオメトリをストリームに出力したり、既存のジオメトリをストリームに書き込まないようにしたりすることで、パーティクルの出現と消滅 (存続期間) を制御します。バッファーに出力をストリームするジオメトリ シェーダーは、通常のジオメトリ シェーダーとは違う方法で作成する必要があります。

FX ファイル内で使用する場合

//--------------------------------------------------------------------------------------
// Construct StreamOut Geometry Shader
//--------------------------------------------------------------------------------------
geometryshader gsStreamOut = ConstructGSWithSO(compile gs_4_0 GSAdvanceParticlesMain(),
                                                "POSITION.xyz;
                                                NORMAL.xyz;
                                                TIMER.x;
                                                TYPE.x" );

FX なしで使用する場合

//--------------------------------------------------------------------------------------
// Construct StreamOut Geometry Shader
//--------------------------------------------------------------------------------------
D3D10_STREAM_OUTPUT_DECLARATION_ENTRY pDecl[] =
{
    // semantic name, semantic index, start component, component count, output slot
    { L"POSITION", 0, 0, 3, 0 }, // output first 3 components of "POSITION"
    { L"NORMAL", 0, 0, 3, 0 }, // output the first 3 components of "NORMAL"
    { L"TIMER", 0, 0, 1, 0 }, //  output the first component of "TIMER"
    { L"TYPE", 0, 0, 1, 0 }, //  output the first component of "TYPE"
};

CreateGeometryShaderWithStreamOut( pShaderData, pDecl, 4, sizeof(PARTICLE_VERTEX), &pGS );

パーティクルのタイプ

このパーティクル システムは、さまざまなプロパティを持つ 5 つのパーティクル タイプで構成されています。どのパーティクル タイプも固有の速度と動作が定義されており、他のパーティクルを生成するタイプもあれば、生成しないタイプもあります。

発射筒 (Launcher) パーティクル

発射筒パーティクルは、移動することも、消滅することもありません。単純に、花火弾 (Shell) パーティクルを生成できるようになるまでカウントダウンを続けます。花火弾パーティクルを生成すると、自身のタイマーをリセットします。

花火弾 (Shell) パーティクル

花火弾パーティクルは、発射筒パーティクルによって不定の速度で夜空に発射される、単一のパーティクルです。これらは、破裂前の花火の砲弾を表すように作成されています。花火弾パーティクルは、ライフスパンがきても、再びシステム内で自己生成することはありません。代わりに、複数の残り火 1 (Ember1) および残り火 2 (Ember2) タイプのパーティクルを生成します。

残り火 1 (Ember1) パーティクル

残り火 1 パーティクルは、花火弾パーティクルが破裂したときに花火弾パーティクルから生成されます。残り火 1 パーティクルのライフスパンは短く、タイマーのカウントダウンにともなってフェード アウトします。タイマーがゼロになっても、システム内で自己生成することはなく、事実上 "消滅" します。

残り火 2 (Ember2) パーティクル

残り火 2 パーティクルも、花火弾パーティクルが破裂したときに花火弾パーティクルから生成されます。残り火 1 パーティクルと異なり、残り火 2 パーティクルはライフスパンがくると残り火 3 パーティクルを生成します。残り火 2 パーティクルは、システム内で 2 次的な破裂を引き起こします。

残り火 3 (Ember3) パーティクル

残り火 3 パーティクルは、残り火 1 パーティクルと似ていますが、残り火 1 パーティクルよりライフスパンが短く、色も異なります。

ジオメトリ シェーダーでのパーティクルの処理

パーティクルは、ジオメトリ シェーダーによって GPU 上で完全に処理されます。ラスタライザーに転送される代わりに、ジオメトリ シェーダーに渡された頂点データは、別の頂点バッファーに出力されます。頂点バッファーが発射筒タイプのパーティクルで初期シードされると、システムは、CPU から送信されるフレーム単位のタイミング情報のみを使用して GPU に留まることができます。

サンプルでは、頂点データで構成される 3 つのバッファーを使用して、花火のパーティクル システムを円滑に処理しています。最初のストリームには、システムの "シード" に必要な初期パーティクルが含まれます。このストリームは、パーティクルシステムがアクティブな間、最初のフレームが一度だけ使用されます。2 番目と 3 番目のバッファーはピンポン バッファーであり、交互に他のすべてのフレームにストリームされ、これらのフレームからレンダリングされます。

パーティクル システムは、次のように動作します。

1. シード バッファーに初期発射筒パーティクルが格納されます。

Bb205329.d3d10_sample_particlesgs_step1(ja-jp,VS.85).gif

2. ジオメトリ シェーダー (GS) の 1 回目の処理では、発射筒のタイマーは 0 であり、発射筒の位置で花火弾が生成されます。注:パーティクル システムは GS 全体を通してすべてのパスでリビルドされるため、新しいパーティクルだけでなく、次のフレームに必要なパーティクルもすべて生成する必要があります。

Bb205329.d3d10_sample_particlesgs_step2(ja-jp,VS.85).gif

3. 2 回目の GS の処理では、発射筒と花火弾のタイマー カウントが減らされ、花火弾が新しい位置に移動します。

Bb205329.d3d10_sample_particlesgs_step3(ja-jp,VS.85).gif

4. 花火弾のタイマー カウントが 0 になります。つまり、これがこの花火弾の最後のフレームになります。

Bb205329.d3d10_sample_particlesgs_step4(ja-jp,VS.85).gif

5. タイマーが 0 になった花火弾は再び生成されません。代わりに、4 つの残り火パーティクルが生成されます。

Bb205329.d3d10_sample_particlesgs_step5(ja-jp,VS.85).gif

6. 発射筒はタイマーが 0 になったので、別の花火弾パーティクルを生成する必要があります。残り火パーティクルはそれぞれ新しい位置に移動し、タイマー カウントも減らされます。発射筒のタイマーはリセットされます。

Bb205329.d3d10_sample_particlesgs_step6(ja-jp,VS.85).gif

出力されたパーティクルの数

ジオメトリ シェーダーは、フレームごとにさまざまな量のデータを生成できます。そのため、このサンプルには、特定の時点でバッファーに存在するパーティクルの数を知る方法がありません。標準の Draw 呼び出しを使用する場合は、GPU に描画させるパーティクルの数を推測する必要があります。幸いなことに、DrawAuto はこのような状況を処理するために設計されています。DrawAuto を使用すると、ストリーム出力バッファーに書き込まれた動的なデータ量を、描画呼び出しの入力データ量として使用できます。これは GPU で実行されるため、CPU ではパーティクル システムを構成する実際のパーティクル数がわからなくても、処理を進めてパーティクル システムを描画することができます。

パーティクルのレンダリング

gsStreamOut ジオメトリ シェーダーによるパーティクルの処理が終了すると、2 番目のパスでは、その出力をちょうど受け取ったバッファーを使用して、パーティクルがポイント スプライトとしてレンダリングされます。VSSceneMain 頂点シェーダーが、パーティクルのタイプと存続時間に基づいて、サイズと色を割り当てます。次に、GSSceneMain が、渡されたすべての点に対する 2 つのトライアングル ストリップを生成することによって、各点からポイント スプライトを作成します。このパスでは、ジオメトリ シェーダーの出力はラスタライザーに渡され、バッファーにはストリーム出力されません。