前回パーティクルの仕組みを作ったので今回はその続き
前回からの課題は、パーティクルを追加とともにシェーダ作成も行っていたので、当たり前なんだけどシェーダ作成は切り離すことにした。
パーティクルシェーダ登録
これまでのシェーダは基本的には固定で、決められたデータを追加するとそれに応じて描画されていた。
パーティクル用のシェーダはそれぞれがカスタムシェーダで、パーティクルの種類毎にシェーダが増えていくことになる。
システムから渡される固定のリソースは、カメラ、パーティクルオブジェクトのWorldマトリックス、時間。
これに加えて前回は入れてなかったカラー、ID、インデックスも追加した。
オプションでサンプラ、テクスチャ、ピクセルシェーダソースを指定できるようにした。
炎のエフェクト
パーティクルの仕組みが出来たので、なにか作ってみようと思う。
定番の炎にチャレンジする。
よく見るのが、ノイズのテクスチャを用意してUVスクロールさせるというもの。
まず、セルラーノイズを動的に作成してテクスチャとして登録する。
パーティクルは適当に散らばせて、上昇させる。
表面にノイズテクスチャを貼り付けるとこんな感じ。
シェーダ
Out.Col = Tex.Sample( Sampler, ( In.UV * 0.5f )) ;
![]() |
| テクスチャを貼り付けただけ |
全部同じ見た目になってるので、ランダムにテクスチャの表示位置をずらして、タイマーでUVスクロールをする。
distanceを使って、円形にくり抜く。
シェーダ
float d = 1 - distance( float2( 0.5, 0.5 ), In.UV ) ; float2 r = rand( In.ID * 0.0001 ) ; Out.Col = Tex.Sample( Sampler, ( In.UV * 0.5f ) + r + float2( 0.0f, Param.Time*0.3 )) ; Out.Col.a *= step( 0.5, d ) * d ;
![]() |
| 丸くなった |
登録時に色をランダムに設定する。(赤は192~255、緑は96~159、青は64~95)
また、更新タイミングで全体を減らしていく。
アルファは時間により255~0にする。
Emissiveにも同じように書き込む。
シェーダ
float d = 1 - distance( float2( 0.5, 0.5 ), In.UV ) ; float2 r = rand( In.ID * 0.0001 ) ; float c = Tex.Sample( Sampler, In.UV * 0.5f + r + float2( 0.0f, Param.Time*0.3 )).r ; Out.Col = In.Col * step( 0.5, c ) * c ; Out.Col.a *= step( 0.5, d ) * d ; Out.Emissive = Out.Col ;
![]() |
| それっぽくなった |
ランダムのパラメータにIDを指定しているが、これをインデックスにすることにより、別のオブジェクトが消えたときにインデックスがずれて、テクスチャの表示する場所がワープすることにより、メラメラ感が増す。
パフォーマンス
調子に乗って連打しまくっていると、FPSがどんどん落ちて30台まで落ちた。
5000パーティクルぐらいでこれだと、先が辛そう。
1パーティクルに1スレッドをその場で生成してそれぞれ更新処理を任せてみたけど、1スレッドだけで処理したほうが速かった。
パーティクル専用スレッドを1つ用意して、処理対象を2つに分けてメインスレッドと分担するようにしてみたら、多少マシになった。
試しにリリースビルドでやってみたら60FPSから落ちることもなく、シングルスレッドでも60FPS維持してた。速くなることは確認できてるので、パーティクル専用スレッドはそのまま採用することにする。
まとめ
初めてエフェクト作ってみたけど、意外とすごいのが出来た。
エフェクトは1つだけでなく、複数のエフェクトを重ねて1つのエフェクトを作ってるみたいで、例えば今回の炎にしても、別途煙を炎の消えたあたりからモクモク出すといいらしい。




0 件のコメント:
コメントを投稿