執筆者:廣瀬
映像業界を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にエフェクト関連の業務を担当。 好きな食べものはソフトクリーム。
TECH BLOG
こんにちは。モノリスソフト テクニカルアーティストの廣瀬です。
今回はMayaのパーティクルでシミュレーション不要なアセットを作ってみたいと思います。
ちょっと裏技的なMayaのパーティクルの使い方を知りたい方や、MayaのExpressionについて勉強したい方は読んでみてください!
サンプルファイル(monolithtech_particle.zip 31.1 KB)はこちらです。
まず初めにアセットに必要なコントローラーを作成したいと思います。
メニューバーからCreate > NURBS Primitives > Circleを実行します。
円形のNURBS Curveが作成されます。
作成したnurbsCircle1をctrl+Dで2つ複製し、アトリビュートを以下のように設定し画像のような感じにします。
ノード | アトリビュート | 値 |
---|---|---|
nurbsCircle2 | rotate | 90, 0, 0 |
nurbsCircle3 | rotate | 0, 0, 90 |
NURBS Curveをすべて選択し、メニューバーからModify > Freez Transformationsを実行します。
以下のコマンドをスクリプトエディタのPythonタブから実行します。
cmds.parent('nurbsCircleShape2', 'nurbsCircle1', add=True, shape=True) cmds.parent('nurbsCircleShape3', 'nurbsCircle1', add=True, shape=True) cmds.delete('nurbsCircle2', 'nurbsCircle3')
nurbsCircle1に次のアトリビュートを追加します。
アトリビュート | 型 |
---|---|
seed | int |
count | int |
startFrame | float |
startFrameRandom | float |
gravity | float |
speed | float |
speedRandom | float |
こちらは単純にロケーターで作成します。メニューバーからCreate > Locatorを実行します。
このロケーター位置からパーティクル位置の方向へパーティクルが飛んでいきます。
弓で表すと、ロケーターが引き絞った手で、パーティクルが矢のようなイメージです。
今後必要になるノードを作成しておきます。
まずは、ノードエディタを開きTabキーからpointMatirxMultノードを4つ作成します。
nurbsCircle1.worldMatrix[0]アトリビュートから各pointMatirxMultノードのinMatrixアトリビュートに接続します。
各pointMatirxMultノードのアトリビュートを以下のように設定します。
ノード | アトリビュート | 値 |
---|---|---|
pointMatirxMult1 | inPoint | 1, 0, 0 |
vectorMultiply | on | |
pointMatirxMult2 | inPoint | 0, 1, 0 |
vectorMultiply | on | |
pointMatirxMult3 | inPoint | 0, 0, 1 |
vectorMultiply | on | |
pointMatirxMult4 | inPoint | 0, 0, 0 |
vectorMultiply | off |
続いて同様に、pointMatirxMultノードを1つ作成します。
locator1.worldMatrix[0]アトリビュートから各pointMatirxMultノードのinMatrixアトリビュートに接続します。
pointMatirxMultノードのアトリビュートを以下のように設定します。
ノード | アトリビュート | 値 |
---|---|---|
pointMatirxMult5 | inPoint | 0, 0, 0 |
vectorMultiply | off |
つぎにパーティクルを作成します。
メニューバーのセットをFXにしてメニューバーからnParticles > Create Emitter (Legacy Particles)を実行します。
この際作成するのは、nParticlesではなくLegacy Particlesであることに注意してください。
emitter1のアトリビュートを以下のように設定します。
ノード | アトリビュート | 値 |
---|---|---|
emitter1 | rate | 1000*fps (画像の場合は1000*24fpsで24000) |
speed | 0 |
アニメーションフレームを1フレームだけ進めます。
particleShape1のCountの値が1000になっているのを確認します。
1フレームだけ進めた状態で、メニューバーからFields/Solvers > Inital State > Set for All Dynamicを実行します。
すると、パーティクルの初期状態が現在の状態(パーティクルが1000個発生している状態)に設定されます。
アニメーションフレームを開始フレームに戻してもparticleShape1のCountの値が1000であることを確認したら、emitter1は今後不要になりますので削除してしまいます。
particleShape1のアトリビュートを以下のように設定します。
こうすることで、後に作成するパーティクルエクスプレッションのCreationがアニメーションフレームが変わるたび毎回実行されるようになります。
ノード | アトリビュート | 値 |
---|---|---|
particleShape1 | startFrame | 1000000 |
particleShape1のアトリビュートを以下のように設定して描画方法を変更しておきます。
ノード | アトリビュート | 値 |
---|---|---|
particleShape1 | particleRenderType | Streak (変更後Current Render Typeボタンをクリックします) |
colorAccum | on | |
lineWidth | 10 |
つぎにパーティクルエクスプレッションを作成します。
Per Particle (Array) Attributesのアトリビュートの上で右クリック > Creation Expression...から以下のエクスプレッションを入力します。
エクスプレッションの処理についてはExpression内のコメントを参照してください。
proc vector mnlsPointMatrixMult(vector $ip, float $im[]) { /* pointMatrixMult melコマンドは、引数と戻り値がfloat配列型で扱いずらいためvector型のプロシージャを定義します。 */ vector $o = <<$im[0]*$ip.x + $im[4]*$ip.y + $im[8] *$ip.z + $im[12], $im[1]*$ip.x + $im[5]*$ip.y + $im[9] *$ip.z + $im[13], $im[2]*$ip.x + $im[6]*$ip.y + $im[10]*$ip.z + $im[14]>>; return $o; } int $id = particleShape1.particleId; int $count = nurbsCircle1.count; if ($id < $count) { // パーティクルの固有IDがcountより少ない場合のみ計算を行います。 int $seed = nurbsCircle1.seed; // シード値を固定します。 seed($seed); seed($id + rand(10000)); float $fps = 24; // 使用するシーンでのfps float $frame = frame; // 現在のフレーム float $startFrame = nurbsCircle1.startFrame; float $startFrameRand = nurbsCircle1.startFrameRandom; float $startFrameMin = $startFrame - ($startFrameRand * 0.5); float $startFrameMax = $startFrame + ($startFrameRand * 0.5); float $startFrameMinMax = rand($startFrameMin, $startFrameMax); // 開始フレーム float $t = max(0, ($frame - $startFrameMinMax) / $fps); // 開始フレームからの経過時間(秒) float $speed = nurbsCircle1.speed; float $speedRand = nurbsCircle1.speedRandom; float $speedMin = $speed - ($speedRand * 0.5); float $speedMax = $speed + ($speedRand * 0.5); float $speedMinMax = rand($speedMin, $speedMax); // パーティクルの速度 float $g = nurbsCircle1.gravity; // 重力 float $mat[] = {pointMatrixMult1.outputX, pointMatrixMult1.outputY, pointMatrixMult1.outputZ, 0, pointMatrixMult2.outputX, pointMatrixMult2.outputY, pointMatrixMult2.outputZ, 0, pointMatrixMult3.outputX, pointMatrixMult3.outputY, pointMatrixMult3.outputZ, 0, pointMatrixMult4.outputX, pointMatrixMult4.outputY, pointMatrixMult4.outputZ, 1}; // nurbsCircle1のワールドマトリクス vector $aim = <<pointMatrixMult5.outputX, pointMatrixMult5.outputY, pointMatrixMult5.outputZ>>; // locator1のワールド座標 vector $dp = sphrand(1); // 半径1の球の中側のランダムな位置を返します。 vector $p = mnlsPointMatrixMult($dp, $mat); // ランダムな位置をnurbsCircle1の位置・回転・スケールに合わせて移動します(初期位置)。 vector $v = unit($p - $aim) * $speedMinMax; // パーティクルの飛ぶ方向と速度を計算します(初期速度)。 if ($frame < $startFrameMinMax) { // 現在のフレームが開始フレーム以下の場合は位置のみ更新して終了します。 particleShape1.position = $p; } else { // 3次元空間からXZ軸とY軸の速度の2次元空間へ変換します。 float $x = sqrt(($v.x*$v.x) + ($v.z*$v.z)); float $y = $v.y; // 2次元空間で斜方投射の計算を行います。 float $vx = $x; float $vy = $y - ($g*$t); float $px = $t * $x; float $py = ($t*$y) - (0.5*$g*$t*$t); $x = max($x, 1.e-12); // 0除算を回避します。 // 2次元空間から3次元空間へ変換します。 vector $op = <<$px * ($v.x/$x), $py, $px * ($v.z/$x)>> + $p; vector $ov = <<$vx * ($v.x/$x), $vy, $vx * ($v.z/$x)>>; particleShape1.position = $op; particleShape1.velocity = $ov; } }
最後にコントローラーのアトリビュートを適当に設定して挙動を確かめてみましょう。
ノード | アトリビュート | 値 |
---|---|---|
nurbsCircle1 | seed | 0 |
count | 100 | |
startFrame | 10 | |
startFrameRandom | 0 | |
gravity | 9.8 | |
speed | 5 | |
speedRandom | 0 |
今回はシミュレーション不要なパーティクルアセットを作ってみました。
シミュレーションの工程がないとエフェクトの作業効率が段違いに上がったりするので、Mayaだけでなく他のアプリケーションでのアセット作りの参考にしてみてください!
執筆者:廣瀬
映像業界を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にエフェクト関連の業務を担当。 好きな食べものはソフトクリーム。