[Omniverse] パーティクルを使う

  • by

前回のPhysicsの続きです。
今回はパーティクルで出来ることを見ていきます。
ここではOmniverse USD Composer 2023.2.3 2022.3.3を使用しました。

USD Composer 2023では、パーティクルの仕様が変わったからか動作しない機能がありました。
そのため、今回はUSD Composer 2022に戻して使用しています。

今回はビルボードパーティクルを見ていくことにします。

パーティクルのドキュメント

https://docs.omniverse.nvidia.com/extensions/latest/ext_particles.html

このドキュメントより、omni.particle.system.coreは大きな変更が行われている最中とのこと。
USD Composer 2022でのパーティクルの機能は今後非推奨になる可能性が高いかもしれません。
ですが、現状はこの手段を使わないと表現できない箇所があるためUSD Composer 2022のパーティクルの機能で説明していきます。

パーティクルの種類

Omniverseで使うパーティクルはFlowを使うものと、ビルボードを使うものの2つがあります。

Flow

流体(Fluid)でリアルな煙や炎を表現できます。

Flowの場合は、鏡にも正しく映り込みます。

ビルボードを使ったパーティクル

リアルタイム系ではパーティクルではビルボードが使われることが多いです。
ビルボードとは、四角形のメッシュが常にカメラ方向を向く表現です。

分かりやすくパーティクルの1つのスプライトを以下のようにしてみました。

動画にすると次のような感じです。
カメラの向きに合わせてパーティクルのスプライトが正面を向いているのが分かります。

OmniverseのレンダリングはレイトレーシングのRTXを使い、パーティクルで使用されるSprite1つ1つもレイトレーシングで描画されます。
しかし、カレントカメラから見たときの1次レイでのパーティクルのみがそのカメラを向く点に注意してください。
鏡に映り込んだパーティクルが正面を向いているわけではありません。

パーティクルの仕組み

それでは、ビルボードパーティクルを配置していきましょう。
まず、パーティクルを発生させるときの仕組みを理解する必要があります。
パーティクルはMeshの面や球の表面から法線方向に放出されます。
この発生源をEmitterと呼びます。

煙や炎などは平面から放出することになります。
爆発や花火などは球からパーティクルを飛ばすのがよさそうです。
Omniverseのパーティクルはデフォルトでは面に対して垂直に(法線方向に)放出されます。
これを垂直からずらして角度を付けて放出したりもできます。

パーティクル1つ1つの粒子はSpriteが使われます。
これはテクスチャを貼り付けた「板」(四角形ポリゴン)です。
ここに煙のモヤモヤのテクスチャなどを割り当てて全体で煙らしくすることになります。

それぞれのパーティクルはこのSpriteのテクスチャ以外にサイズや寿命、速度などが割り当てられます。
また、時間によって徐々に透明化、色変更、サイズ変更、Collisionによる衝突を与えることもできます。
パーティクルの動きの計算はSolverというのが使われます。
Omniverseでは、Omni Graphというのを使用してノードベースでパーティクルを制御していくことになります。

それでは、ここでは煙を表現してみましょう。
まずは素材(Spriteに割り当てるテクスチャ)を作成します。

Spriteの素材を作成

Affinity Photo2

2DペイントツールでSpriteの素材を作っていきます。
ここではAffinity Photo2( https://affinity.serif.com/ja-jp/photo/ )というのを使って、まずベース部を作成しました。
ノイズ(雲模様)を円状にトリミングして周囲をぼかしています。
また、アルファで抜いています。
単色の黒とアルファのみを使ってます。

パーティクルで使う場合はこのときのノイズ模様は細かくするよりも粗めのほうがよいかもしれません。
これの白色と黒色をアルファ付きの256×256ピクセルの2つのpngとして出力しました。

ダウンロード : NoisePattern_01.zip

Spriter Pro

これだけではのっぺりしてしまいますので、Spriter Pro ( https://store.steampowered.com/app/332360/Spriter_Pro/?l=japanese )という2Dアニメーション専用のツールで最終的なスプライト素材になるように加工していきました。
この手のツールとしては、Spine ( https://ja.esotericsoftware.com/ )が有名かと思います。
このような2Dアニメーション専用のツールを使う重要な目的として、タイリングされたアニメーションの画像をほしい、というのがあります。
これらは後述します。

白と黒のノイズ模様の画像を読み込み、黒のノイズ模様の上に白のノイズ模様が来るようにDepthを調整。
黒のノイズ模様は右下に少しずらしています。
これにより、わずかに影ができてボリュームがあるように見えるようにしました。
また、アニメーションとしてアルファ1.0から0.0で徐々に消えていくようにキーフレームを割り当て。

Spriterのエクスポートで、スプライトのアニメーションを1テクスチャにパックします。
テクスチャの左上から右下に向かって8×8個を並べました。

ダウンロード : smoke_8x8.zip

png形式で保存しています。
このようなテクスチャを用意することで、パーティクルをEmitterから発生させたときに個々のパーティクルの寿命に合わせてこのアニメーションが再生されることになります。
なお、Omniverse上のパーティクルの機能として不透明度(Opacity)を別途与えることは可能です。
スプライトで別段透過アニメーションさせなくても問題ないのですが、ここではタイリングの説明を入れておきたいためこのようにしています。

この2Dスプライトの作成と調整が非常に重要です。
もし、スプライトのテクスチャで特徴的な模様がある場合はそれが如実に出てしまいます(これじゃない感が強くなる)。
このテクスチャが主張しないように微調整するのは、Affinity Photo – Spriter Pro – Omniverse(パーティクルの表示)で行きして違和感をつぶしていくようにしました。
これで素材ができたのでUSD Composerに進みます。

USD Composer : パーティクルの配置

USD Composer 2022.3.3を使用しました。
レンダリングはRTX-Real-Timeを使用します。

Render Settings

パーティクルは半透明を多用するため、まずRender Settingsを確認する必要があります。
Enable Fractional Cutout OpacityチェックボックスをOnにします。
これを有効にしないと、RTX-Real-TimeではOpacityが1.0以外は完全不透明になってしまいます。
また、TranslucencyのMax Refraction Bouncesを6から20に変更しました。

Max Refraction Bouncesが6の場合と20の場合の比較は以下のようになります。

Max Refraction Bouncesが6の場合はレイの通過が6回で止まってしまうため、黒い箇所が見えています。

Emitterを配置

MeshのPlaneを配置し、Scaleを0.1としました。
これをパーティクルのEmitterとします。

Planeを選択した状態で、メニューの[Create]-[Particles]-[with Geometry Replicator]を選択します。
これにより、面の表面からパーティクルが放出されます。

ビューポートでは上向きに白色のパーティクルが放出されています。
StageにはOmni Graph/ParticleSystem、output、Looks/particlesが生成されました。

要素名 説明
Omni Graph/ParticleSystem パーティクルを制御するノード群
output 生成されたパーティクルのMeshが出力される
Looks/particles パーティクルで参照するマテリアル

パーティクルの制御はOmni Graphで行います。
いくつか調整していきます。

Omni Graph/ParticleSystem/emitter


Emitterでは以下の要素を調整しました。

要素名 説明
Spawn Rate/Spawn Rate per Second 1秒間に放出するパーティクル数
Lifespan/Lifespan 寿命(秒)
Direction、Speed And Mass/Random Direction 垂直(Y-upの場合は法線方向を(0, 1, 0)とする)からずらすベクトル
Direction、Speed And Mass/Speed 放射速度
Display/Particle Size パーティクルのサイズ
Orientation/Random Orientation パーティクル放出時にランダムに回転

Spawn Rate per Secondはデフォルト10。50に変更すると以下のようになりました。

Lifespanはデフォルト1.0(秒)。2.0に変更すると以下のようになりました。

Random Directionはデフォルト(0.0, 0.0, 0.0)。円錐状にするために(0.5, 0.0, 0.5)としました。

これで、放射はXZ方向に広がります。

Particle SizeのXYZはデフォルト(5.0, 5.0, 5.0)。(20.0, 20.0, 20.0)に変更すると以下のようになりました。

また、発生から寿命の間でこのサイズを動的に変更可能です。
その調整はOmni Graphで行います。これについては後述します。

Random Orientationはテクスチャを貼らないと分かりにくいため、テクスチャを貼って確認しました。
Random Orientationはデフォルト(0, 0, 0)。これを(0, 300, 0)に変更すると以下のようになりました。

スプライトのパーティクルが放出時にランダムに0-300度回転します。
放出後は回転は変わりません。
与えるパーティクルのテクスチャは1種類(アニメーションはさせますが)のため、Random Orientationにより再利用しているのがばれにくくなります。
最終的には一周の360(度)の指定で問題なさそうです。

パーティクルのマテリアルを調整

Affinity Photo + Spriter Proで描いた煙模様のテクスチャ(png)をパーティクルに与えます。
Stageウィンドウで/World/Looks/particlesを選択。
Propertyを編集します。

TextureチェックボックスをOn、後々パーティクルの寿命に合わせて色を変えたいのでVertex Colors(Base)チェッボックスをOnにします。

Bitmap parametersのBitmap fileでパーティクル用のテクスチャを指定します。

これでパーティクルはテクスチャに切り替わりました。

しかし、まだタイリングの調整をしていないため8x8に配置したテクスチャがそのまま表示されています。
これはOmniGraphで調整することになります。

OmniGraphでパーティクルを調整

これ以降はParticleSystemのOmniGraphを調整します。

  • タイリングされたパーティクルのテクスチャを指定
  • 寿命に合わせてパーティクルサイズを大きくしていく
  • 寿命に合わせてパーティクルの色を変える(煙のように白から黒にグラデーション)

他にもパーティクルとメッシュの衝突判定もOmniGraphで指定できるのですが、
長くなるのでそれは次回に行います。

OmniGraph(Particle Editor)を表示

StageウィンドウでOmniGraph内のParticleSystemを選択。
右クリックしてポップアップメニューからOpen Graphを選択します。

Particle Editorにグラフが表示されます。

ここで大事な流れは,emitter → solver → geometry_replicatorのところです。

emitterでパーティクルが発生しsolverで計算、最終的にgeometry_replicatorで出力されることになります。

st_panner : パーティクルのテクスチャのタイリングを指定

次にパーティクルのテクスチャを使ったタイリングによるアニメーションを行うために、OmniGraphにノードを追加します。
テクスチャは描画になりますのでsolverで計算された後のビジュアルとしての指定を行います。
OmniGraph上では"st_panner"というのを使用します。
NodesからParticle – st_pannerを探します。
ノード一覧の一番目のSearchでst_pannerを検索しても構いません。
これをグラフにドラッグして配置します。

solver – geometry_replicatorの接続を切り(接続のラインを選択し右クリック。Disconnectを選択)、
solver – st_panner – geometry_replicatorとなるように接続します。

st_pannerを選択し、Propertyでテクスチャに合わせたタイリングの横と縦の個数をImage Tilesで指定します。
ここではImage Tile Columnsに8、Image Tile Rowsに8を指定しました。

これで、パーティクルの発生から消滅にかけてテクスチャで指定したアニメーション(アルファが0.0に近づく=徐々に消える)が表現できました。

この段階でemitterのパラメータを調整し、煙らしくします。
以下を調整しました。

  • Spawn Rate per Second
  • Lifespan
  • Random Direction
  • Speed
  • Particle Size
  • Random Orientation


パーティクルのサイズは後々OmniGraph側で調整するため(サイズに0.0-1.0を乗算することができる)、今はもっとも大きくなった時のサイズを指定しています。

ramp_modulator : 寿命に合わせてパーティクルサイズを大きくしていく

※ ramp_modulatorは、USD Composer 2023.2.3では正しく動作していませんでした。ここでは2022.3.3で確認しています。

パーティクルが発生して寿命で消えるまでの間で、パーティクルサイズを徐々に大きくしていきます。
実際はemitterのParticle Sizeで最大のパーティクルサイズを指定し、時間に合わせて0.0-1.0の間の乗算値を変化させることになります。
これは、"ramp_modulator"というノードを使用します。

これをst_pannerからgeometry_replicatorの間に接続します。

ramp_modulatorノードを選択し、PropertyウィンドウでColor Rampを確認。
attribute to remapでscaleを選択します。

その下のグラデーションでは赤緑青が指定されていますが、scaleではfloat値の0.0-1.0の倍率を使用することになります。
おそらくRGBのRed要素が使われると思いますが、ここではグレイスケールで黒から白を与えるようにしました。
黒の場合は倍率0.0、白の場合は倍率1.0です。

これで次のようになりました。

ramp_modulator : 寿命に合わせてパーティクルの色を変える

ramp_modulatorでは1つの要素の変化を与えるノードのようですので、
もう一つramp_modulatorノードを接続して、色はここで変化させることにしました。

Propertyでは、Color Rampのattribute to remapでcolorを選択します。

グラデーションでは白から黒に変化させました。
この色は、MeshのVertex Colorが使用されます。テクスチャにこの色が乗算されることになります。

emitterのパラメータも調整し、以下のようになりました。


ライトの影響もパーティクルに反映されているのを確認できます。

今回で、まずは煙をパーティクルで表現できるようになりました。
次回はパーティクルの衝突判定について書いていく予定です。