RendererをDX11.Particlesの周りに絞ってまとめます。自分も全容はまるで把握してませんので、間違い等ありましたらご指摘いただけたらと思います。Rendererこれです。RenderingをDX11Particlesの拡張で実装。1万パーティクル分の軌跡くらいならそこそこのゲーミングノートでもfps30越え安定するのでいい感じ。Labさんありがとうございます。Vol.2の電子書籍版を買ったので、汎用的で使いやすそうなTrailのアルゴリズムを実装してみました。そんななかで、やっぱりTrailといったらパーティクルだろうということで、vvvvのとても便利なparticle系cotributionであるDX11.Particlesの拡張モジュールとしても実装してみることにしました。Bufferによる位置の入力を必要とします。いうわけで、パーティクルの位置情報がどこにあるのかを見るために、DX11.Particlesにおいてどのようにデータが計算され、ノード間を流れていくのかざっくり見ていきます。パッチ例Spin(DX11.Particles.Modifiers)のヘルプパッチです。細かい説明は長くなるので割愛して、データの流れに絞ってみていきます。Emitterモジュール名前の通り、パーティクルをEmit(発射)するモジュールです。Particle構造体を定義し、発生させた各パーティクルにVelocityやAgeなどのパラメータを与えてBufferの形で出力しています。基本的にEmitterノードのいずれかの種類がなければParticleは生まれません。実際の構造体の定義部分のコードです。COMPOSITESTRUCTによる定義がなければ基本的なパラメータのセットが定義されます。ModifierモジュールEmitterで発生したパーティクルに対し、色や速度等、様々な影響を及ぼすのがModifierモジュールです。spin等のMotion系のModifierでは、大体Particle構造体のForceのパラメータに加算するなりそのまま書き換えるなりして影響を与えています。Modifierの内部のForce更新部分はこんな感じです。ParticleSystemモジュールEmitterで発生したパーティクルの情報と、Modifierで与えられた影響をGroupノードでひとまとめにしています。この辺の挙動はよくわかっていないのですが、たぶんGroupでまとめられた2つのシェーダーが一つのコードとして展開されているような状況になっているものだと勝手に考えてます。ここまででまとめられたパーティクルのデータがParticleSystemノードによって、実際に位置やフラグの更新が行われるとともに、サブパッチ内部のRendererノードでGPUのメモリに計算結果が書き出されます。RenderingモジュールParticleSystemノードで書きだされた位置情報や色情報を基に、実際にレンダリングにされるジオメトリ等が用意されます。Rendererもここでの実装になります。このパッチではSpriteノードが使われています。内部では生存しているパーティクルの数をカウントするALIVECOUNTERBUFFERを使って、パーティクルの数分のVertexを生成してシェーダーに入力しつつ、パーティクルのパラメータが格納されているPARTICLEBUFFERを入力してVertexShaderで位置情報などを頂点の位置などとして参照しています。この頂点を基にGeometryShaderでSpriteを生成してレンダリングする流れになっています。つまるところ、ParticleSystemによって出力されたPARTICLEBUFFERに諸々の計算がなされたパーティクルの位置情報が格納されていることになります。生存しているパーティクルのindex情報が格納されたALIVEPOINTERBUFFERを使用して、生存しているパーティクルの位置情報を参照すれば、あとはGeometryShaderなりPixelShaderなりを活用してDX11.ParticlesのカスタムEffectが作れると思います。同様に、ForceやColorなどのパラメータに手を加えればカスタムModifierが作れますし、パーティクル発生時の位置や色などを定義すればカスタムEmitterを作れると思います。infoノードやshiftノードなど、パラメータを取得するのに便利なノードがたくさん用意されているうえに、ほぼすべてのノードにちゃんとしたヘルプパッチが用意されているため、わかる人にはとても作りやすくなっていると思います。ここからは、小ネタ的にTrailの実装ででてきたわかりづらかったところをメモしていきます。Unity→vvvvローカライズUnityWorldToClipPos名前の通り、ワールド座標からクリップ座標に変換するメソッドです。vvvv的にはよくあるこんな感じで行けます。View行列とProjection行列を乗算するものだったので、ローカル座標からワールド座標への変換はvvvv的にはWORLDのセマンティクスを付けた行列を乗算するなりしてワールド座標へ変換しておきます。RWStructuredBufferとStructuredBufferRWStructuredBufferは名前の通り読み書きの両方ができるBufferリソースなので、計算結果を書き込んで次のフレームで同じリソースから値を読むようなシミュレーション的なことに使います。StructuredBufferは読み取り専用なので外部から出力されたBufferリソースを入力するのに用います。シェーダー内でStructuredBufferを宣言すると、ノードにインレットが生成されます。MultiStructuredBuffer通常、vvvvでComputeShaderを使う際は、BUCKBUFFERのセマンティクスが付いたものだけが実際にGPUメモリに書き出されるのですが、これだと不便な状況があったりします。Countが違う2つのBufferを出力する必要がありました。Countなどで出力できます。パーティクルの参照と生死DX11.ParticleはLifeTimeのパラメータが設定できるため、パーティクルが死んだり、その分また新しくパーティクルが生まれたりします。生きているパーティクルのみを参照するためにALIVEPOINTERBUFFERやALIVECOUNTERBUFFERを使ったりするのですが、ALIVEPOINTERBUFFERには、確かに生きているパーティクルのindexが入ってくるのですが、フレームごとにまるでバラバラな値が入ってくるので、これを使うと同じパーティクルを参照し続けることができません。するのかというと、どうもこうもなく安直にPARTICLEBUFFERから直接同一のindexを参照し続けるしか方法がなさそうです。このことで時々おかしな描画が出てきますが、それなりに数を生成するとそこまで気にならないのでこのままにしています。Countの指定には上記の理由でALIVECOUNTERBUFFERを使いたくなるのですが、ALIVECOUNTERBUFFERの値が変化すると、ComputeShader内のBufferが初期化されてしまうみたいで、Trailがリセットされてしまいました。Countが変化しないようにしています。Countを大きく下回らないようにすればさほど気になりません。ところどころ不親切なところがありそうですが、何かあれば適当に質問してもらえたらわかる範囲で回答します。DX11.Particlesはとてもうまいことモジュール化されており、中身を見るだけでだいぶ勉強になるので、普段シェーダーをいじらない人も挑戦してみるといいかもしれません。コメントをキャンセルメールアドレスが公開されることはありません。*サイト次回のコメントで使用するためブラウザーに自分の名前、メールアドレス、サイトを保存する。新しい投稿をメールで受け取る。