Mayaのシミュレーション不要なパーティクルアセットを作ってみる

INDEX

はじめに

こんにちは。モノリスソフト テクニカルアーティストの廣瀬です。

今回はMayaのパーティクルでシミュレーション不要なアセットを作ってみたいと思います。

ちょっと裏技的なMayaのパーティクルの使い方を知りたい方や、MayaのExpressionについて勉強したい方は読んでみてください!

サンプルファイル(monolithtech_particle.zip 31.1 KB)はこちらです。

DOWNLOAD

コントローラーを作成する

まず初めにアセットに必要なコントローラーを作成したいと思います。

パーティクルを発生させる位置を指定するためのコントローラーを作成します。

メニューバーからCreate > NURBS Primitives > Circleを実行します。
円形のNURBS Curveが作成されます。

tech_43_02.png

作成したnurbsCircle1ctrl+Dで2つ複製し、アトリビュートを以下のように設定し画像のような感じにします。

ノード アトリビュート
nurbsCircle2 rotate 90, 0, 0
nurbsCircle3 rotate 0, 0, 90

tech_43_03.png

NURBS Curveをすべて選択し、メニューバーからModify > Freez Transformationsを実行します。

tech_43_04.png

以下のコマンドをスクリプトエディタのPythonタブから実行します。

cmds.parent('nurbsCircleShape2', 'nurbsCircle1', add=True, shape=True)
cmds.parent('nurbsCircleShape3', 'nurbsCircle1', add=True, shape=True)
cmds.delete('nurbsCircle2', 'nurbsCircle3')

tech_43_05.png

nurbsCircle1に次のアトリビュートを追加します。

アトリビュート
seed int
count int
startFrame float
startFrameRandom float
gravity float
speed float
speedRandom float

tech_43_06.png

パーティクルを飛ばす方向を指定するためのコントローラーを作成します。

こちらは単純にロケーターで作成します。メニューバーからCreate > Locatorを実行します。
このロケーター位置からパーティクル位置の方向へパーティクルが飛んでいきます。
弓で表すと、ロケーターが引き絞った手で、パーティクルが矢のようなイメージです。

tech_43_07.png

コントローラーに関する必要なノードを作成する

今後必要になるノードを作成しておきます。

まずは、ノードエディタを開き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

tech_43_08.png

続いて同様に、pointMatirxMultノードを1つ作成します。
locator1.worldMatrix[0]アトリビュートから各pointMatirxMultノードのinMatrixアトリビュートに接続します。
pointMatirxMultノードのアトリビュートを以下のように設定します。

ノード アトリビュート
pointMatirxMult5 inPoint 0, 0, 0
vectorMultiply off

tech_43_09.png

パーティクルを作成する

つぎにパーティクルを作成します。

メニューバーのセットをFXにしてメニューバーからnParticles > Create Emitter (Legacy Particles)を実行します。
この際作成するのは、nParticlesではなくLegacy Particlesであることに注意してください。

tech_43_10.png

emitter1のアトリビュートを以下のように設定します。

ノード アトリビュート
emitter1 rate 1000*fps (画像の場合は1000*24fpsで24000)
speed 0

tech_43_11.png

アニメーションフレームを1フレームだけ進めます。
particleShape1Countの値が1000になっているのを確認します。

tech_43_12.png

1フレームだけ進めた状態で、メニューバーからFields/Solvers > Inital State > Set for All Dynamicを実行します。
すると、パーティクルの初期状態が現在の状態(パーティクルが1000個発生している状態)に設定されます。
アニメーションフレームを開始フレームに戻してもparticleShape1Countの値が1000であることを確認したら、emitter1は今後不要になりますので削除してしまいます。

tech_43_13.png

particleShape1のアトリビュートを以下のように設定します。
こうすることで、後に作成するパーティクルエクスプレッションのCreationがアニメーションフレームが変わるたび毎回実行されるようになります。

ノード アトリビュート
particleShape1 startFrame 1000000

tech_43_14.png

particleShape1のアトリビュートを以下のように設定して描画方法を変更しておきます。

ノード アトリビュート
particleShape1 particleRenderType Streak (変更後Current Render Typeボタンをクリックします)
colorAccum on
lineWidth 10

パーティクルエクスプレッションを作成する

つぎにパーティクルエクスプレッションを作成します。

Per Particle (Array) Attributesのアトリビュートの上で右クリック > Creation Expression...から以下のエクスプレッションを入力します。
エクスプレッションの処理についてはExpression内のコメントを参照してください。

tech_43_15.png

Creation 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

tech_43_01_16.gif

まとめ

今回はシミュレーション不要なパーティクルアセットを作ってみました。
シミュレーションの工程がないとエフェクトの作業効率が段違いに上がったりするので、Mayaだけでなく他のアプリケーションでのアセット作りの参考にしてみてください!

執筆者:廣瀬

映像業界を経てモノリスソフトへ入社。 以来、テクニカルアーティストとして主にエフェクト関連の業務を担当。 好きな食べものはソフトクリーム。

ABOUT

モノリスソフト開発スタッフが日々取り組んでいる技術研究やノウハウをご紹介

RECRUIT採用情報