Epic Gamesが配布している、Unreal Engineの様々な機能のサンプルをまとめたプロジェクト「機能別サンプル(Content Examples)」の中に、UE4.26では、Niagara AdvancedというMapがあります。
そこでは、Niagaraの新機能やそれを使った応用例など、とても参考になるサンプルが多く配置されていて、実装内容を見てみるとたくさんの学びがあります。
それらのサンプルについて、いくつかの記事に分けて解説を行っていきます。

ちなみに、機能別サンプルはEpic Games Launcherのラーニングタブからダウンロードできます。また、UEのバージョン毎に内容が違うので注意してください。
TLDR;)
- Simulation Stageは、「1フレーム内に繰り返し処理ができるParticle Update」みたいなものだよ
- Simulation Stageは、Particleだけでなく、特定のData Interfaceを処理することもできるよ
- Simulation Stageは基礎的な機能なので、これをどう使うが重要だよ
はじめに
この記事では、UE4.26から追加された、Simulation Stageという新機能の基礎的な内容について、Niagara Advancedの「1.1 Simulation Stage Fill Render Target」をもとに、解説していきます。
Simulation Stageは、Niagara Advancedの他の多くのサンプルでも使われているぐらい、根幹となる機能です。コンセプト自体はシンプルなので、何に使うかが重要ですが、まずはSimulation Stageが何なのかについて解説できればと思います。
Simulation Stage とは
Simulation Stageとは、簡潔に言うと「1フレーム内に繰り返し処理ができるParticle Update」だと考えれば大丈夫です。1フレーム内に、そこにのせた処理を指定した回数だけ実行してくれます。

UE4.25以前は、繰り返し処理をするには、Scratch Padの中でHLSLを書くか、Map Forというexperimentalのノードを使うしかなかったと思いますが、それがより簡潔に使えるようになり、さらに既存のParticle Update内で使えるモジュールも、繰り返し処理に入れることが可能となりました。
さらに、Simulation Stageの革新的な点は、処理をParticleに対して行うだけでなく、Render Target 2Dのような特定のData Interfaceに対しても処理ができるようになっていることです。
Data Interfaceについては、サンプルで公開されているRender Target 2DやGrid2D Collectionの他にどのようなものが使えるのかまだ詳しくわかりませんが、ここで使えるData型次第で無限の可能性があるように思えます。
Unityユーザーには、ノードで書けるCompute ShaderがVisual Effect Graphの中に実装されたといったようなイメージでしょうか。(乱暴)
また、Compute Shaderでピンとくる人もいるかもしれませんが、GPU Simでしか使えません。
と、つらつらとSimulation Stageのコンセプトについて述べましたが、具体的な例を見ないとよくわからないと思うので、Niagara Advancedの「1.1 Simulation Stage Fill Render Target」を見ていきたいと思います。
とにかく、Simulation Stageで覚えておけばいいのは以下の3点です。
- 1フレーム内で繰り返し処理ができる
- Particleだけでなく、特定のData Interfaceにも処理を行える
- GPU Simでのみ使える
1.1 Simulation Stage Fill Render Target の解説

Niagara AdvancedのMapの一番はじめにあるサンプルです。
一見すると、ただTextureを貼ったSpriteを表示しているだけのように見えますが、内部ではSimulation Stageを使ってTextureから各テクセルの色を取得し、Render Target 2Dに書き込む処理を行っています。そして、そのRender Target 2DをMaterialに渡して描画しています。
具体的に中身を見ていきましょう。

FillRenderTargetというNiagara Systemの中を開くと、EmitterLevelSpriteRendererというEmitterがあります。内容はシンプルで、いくつかの変数を初期化し、一つのSimulation Stageで処理を実行しています。

まず、Simulation Stageを有効にするために、Emitter PropertiesでGPU Simに切り替え、Fixed BoundsにCheckを入れ、下のSimulation StagesのEnable Simulation Stages (Experimental GPU Only)にもCheckを入れます。


Emitter SpawnとEmitter Updateで、Position, Texture Sample, Render Target 2D, Sprite Sizeを初期化しています。Sprite SizeをEmitter Updateで初期化していますが、Emitter Spawnでやっても問題ありません。

Simulation Stageを追加するには、Add Simulation Stageの横のプラスボタンを押し、Generic Simulation Stageを追加します。

Iteration SourceでParticleかData Interfaceかを選択できます。今回は、Render Target 2Dに対して書き込み処理を行うので、Render Target 2DをData Interfaceとして設定しています。
Iterationsは繰り返し回数です。
Emitter Reset Onlyにチェックを入れると、EmitterがResetされた最初のフレーム時のみ処理を実行するようになります。
Simulation Stage Nameに入れた名前が、Stackの名前として表示されます。

Copy Texture To Render Targetは、名前を変えただけで、もとはScratch Padモジュールです。ダブルクリックで中身を見てみます。


まず、インプットはRender Target 2DとTexture Sampleの2つです。
ここで重要なのは、Execution Indexです。今回は、Data InterfaceにRender Target 2Dを指定したので、ここでのExcution Indexは、Render Target 2Dのテクセルの通し番号になります。最初にRender Target 2Dを512×512で初期化したので、Execution Indexには、0~262,143の値が入ってきます。
それを、Linear to Indexで、0~262,143から(0,0), (0, 1) ~ (511, 510), (511, 511)といった、2次元のインデックスに置き換えています。
さらに、Get Render Target SizeでRender Target 2Dの縦横のサイズを取得し、

置き換えたインデックスを割ることで、0~1のUV値に変換。そのUV値からSample Texture 2Dで、入力されたTexture Sampleの各色情報を取得します。((0.5, 0.5)が足されていますが、これは間違いなので、ない方が正確です。誤差なのであまり問題はないですが。)

最後に、取得した色情報をRender Target 2Dの該当のテクセルに書き込みます。
これで、Texture SampleをそのままRender Target 2Dにコピーすることができました。
あとは、このRender Target 2DをMaterialに渡すだけですが、ここでさらに4.26からの新機能があります。

Sprite RendererのBindingsの項目を見ると、Material Parameter Bindingsというものが、追加されています。ここから、Materialの公開しているparameterに値を入れることができます。
え、これもうDynamic Parameter必要なくなるの?と思ったのですが、ここに設定できるAttributeはEmitterかSystemのものに限られるみたいで、Particle毎に色を変えたい時などは、まだDynamic Parameterを使う必要があります。
いずれにせよ、今回はこれを使って、Render Target 2DをMaterialに渡しています。Material内での処理の解説は省きますが、基本的には受け取ったTextureをそのまま表示しているだけです。
これで、Simulation Stageの使用例としての解説は終わりですが、最後に一点だけ解説してないことがあります。
そもそも、今回Particleを一個も出していないのに、なぜSpriteが表示されているのかという話ですが、これも4.26からの新機能で、Emitterの情報をもとにParticleを表示するということができます。

よく見ると0 Particlesとなっています。

これは、Sprite Rendererのところで、Source ModeをEmitterに変えると、BindingsがEmitterのAttributeになります。これでEmitterのAttributeの情報をもとにParticleを出すことができます。
そのため、今回最初にEmitterのAttributeであるpositionやsprite sizeを初期化していたのでした。
おまけ
これでSimulation Stage Fill Render Targetの解説は以上です。
このサンプルは、Simulation Stageの使用方法の説明のためにあまり意味のないことをしているだけのように見えますが、こんなことが簡単にできるよというおまけだけ紹介しておきます。

単純に、Render Target 2Dの大きさを512×512から32×32にするだけて、ピクセルアートのような見た目に簡単にすることができます。
このような形で、元画像を色々加工して表示するのもSimulation Stageの一つの使い道になります。それについては、Grid2D Collectionとの組み合わせでより複雑なことができるようになります。その辺りの解説は、また別の記事で紹介したいと思います。
おわりに
今回は、Simulation Stageの解説でしたが、これはNiagaraの新機能の根幹となる機能で、個人的にもかなり革新的なものだと思っています。
今後また別の記事で詳しく解説していくことになりますが、2D流体シミュレーションや、Position Based Dynamics、Neigbor Grid 3Dなど様々な機能で使われています。発想次第で無限の使い道があるので、触ってみて色々応用してみると楽しいのではと思います!
One Comment