SOPのみで作る慣性方程式を利用した溶岩噴出のエフェクト(遠景用)

# はじめに
始めましてフジヤマタカシと申します。エフェクトアーティストになりたくてHoudiniを勉強しています。

今回は難解な漫画チュートリアルを描かれているFujtiaさんの、中でも特にわかり難いと言われる[放物線運動](http://blog.livedoor.jp/fujitarium/archives/52274094.html)を勉強して、何とか理解できる段階まで来たので、それを利用したマグマ噴出のエフェクトの作り方を紹介してみようと思います。よろしくお願いします。

# 単体の噴出セッティング
先ずは適当にCircleか何かにの平面にScatterでポイントを作ります。

それに対し初速度自由落下方程式をWrangleで書いて動きをつけます。

```
@age = @Frame * @Timeinc;
@v = vector( onoise(@P * 0.5 ,5,0.5,1) )*5 + set(0,5,0);
vector g = set(0,-9.8,0);
@P += @v * @age + g*pow(@age,2)*0.5;
```

適当にノイズを足しましたが平面に歪みが掛かったような微妙な広がりに。
![Screenshot from 2018-12-01 17-28-30.png](https://qiita-image-store.s3.amazonaws.com/0/321297/56454dd2-98d1-04b3-03ae-19b345ae2bb0.png)


ここで一旦初期速度のスピード成分と方向成分に分けて考えたいと思います。
この微妙な広がりは平面ポジションからノイズの成分を取得しているからです。
まずはスピード成分はランダムな値を入れて、遠くに飛ぶポイントとあまり飛ばないポイントが混在させます。
`float speed = rand(@ptnum);`
それから方向成分にノイズを掛けたいと思いますが、このときにスピード成分のランダム値をノイズ取得時のY座標として扱います。

```
@age = @Frame * @Timeinc;
float speed = rand(@ptnum);
vector dir = vector(onoise(@P + set(0,speed * 0.5,0) ,5,0.5,1)) + set(0,1,0) ;
@v = dir * fit01(speed,0.5,1) * 10;
vector g = set(0,-9.8,0);
@P += @v * @age + g*pow(@age,2)*0.5;
```

![Screenshot from 2018-12-01 17-30-33.png](https://qiita-image-store.s3.amazonaws.com/0/321297/0cc10473-7602-a4ab-380f-5685e7fffd5b.png)
この形状に対してもう少し編集してマグマっぽくしていきます。

スピード成分をノイズに考慮させずにパーティクルをスパイク形状に発射させたりもできますが、これは今回求めてる形状ではありません。
![Screenshot from 2018-12-01 17-38-20.png](https://qiita-image-store.s3.amazonaws.com/0/321297/71994ccc-8f44-d72a-b7f4-04e5bb1b4668.png)

```
@age = @Frame * @Timeinc;
vector dir = normalize( @P-set(0,-1,0) );
float speed = rand(@ptnum)* fit( onoise(@P * 3 ,5,0.5,1), -0.5, 0.5 , 0, 1);
@v = dir * speed * 20;
vector g = set(0,-9.8,0);
@P += @v * @age + g*pow(@age,2)*0.5;
```

# クリッピングとカラーアトリビュートのセッティング
これをクリッピングして形状をもう少しよくするのと溶岩っぽい色をつけます。
この作業は発射されたポイントの特定のフレームにて行いたいと思います。
ここがPOPを使わずに方程式アニメーションの利点になります。

初速度自由落下方程式の部分は今後何度か使う事になるのでファンクション化しておきますね。

```
vector particle(vector v, gravity; float age){
    return v * age + gravity*0.5*pow(age,2);
}
```

このようにすると初速度自由落下のポジションを取得できるわけです。
`@P = @P + particle(@v, g, @age);`

クリップを色のノイズをパーティクルが発射されてから32フレームの位置をrestとして扱いたいので、ageの部分が32.0/24となります。
`vector rest = @P + particle(@v, g, 32.0/24);`

ノイズを調整してremovepointでポイントを消してこのような感じになります。

```
float noise =  abs( onoise(rest*set(1,0.75,1)*0.25 ,3,0.4,1 ) );
if(noise>0.005)removepoint(0,@ptnum); 
```

![Screenshot from 2018-12-01 18-24-17.png](https://qiita-image-store.s3.amazonaws.com/0/321297/c4ba4bbc-d02c-ced1-11bf-7fa0b5ed34b0.png)

次にCdアトリビュートを設定しますが、ageベースでランプを使い黄色＞赤＞茶色＞黒を色変化させますがノイズを使って温度を長く保つ部分と、早く冷める部分の分布を行いたいと思います。

```
float colorNoise = abs( onoise(rest*set(1,0.75,1)*0.25 + 100 ,3,0.4,1 ) );
@Cd = chramp("color",fit(@age,0, fit(colorNoise,0.4,0,0.5,15), 0, 1 ));
```

![Screenshot from 2018-12-01 18-48-25.png](https://qiita-image-store.s3.amazonaws.com/0/321297/eeb5aa77-3932-e394-f11c-717719e2e6eb.png)


# アニメーションをサイクルさせる
次にこの方程式アニメーション持続して噴出させるためにサイクルさせたいと思います。
このあたりはFujitaさんの[Rain ripplalt](http://blog.livedoor.jp/fujitarium/archives/52442698.html)を参考にさせていただいてます。

この設定では90フレームくらいで全て地面の位置に落ちているので余裕を持って120フレームサイクルにさせます。
このために`@age = @Frame * @Timeinc;`としていたものを
`@age = ((@Frame/120) - floor(@Frame/120) ) * 120 * @Timeinc`とします。
この式により120フレーム毎にageが0にリセットされます。
この時にサイクル毎にノイズの形状を変化させてバリエーションを出したいので。サイクル毎のユニーク数値を取得するために。
`int cycleCount = floor(@Frame/120);`を付け加えておきます。
更にマグマを連続して噴出してるように見せるため複数の噴射をオーバーラップさせたいので
これを4つのグループに分けて30フレーム毎の差をつけて行きたい噴出させます。
その為にポイント数を4倍にした上で`int iteration = @ptnum%4;`とします。
この変数を使えば@Frame - iteration*30で@ageのずれを出せます。

ちょっと整理してみましょう。

```
int iteration = @ptnum%4;
float frameOffset = @Frame - iteration*30;
@life = 120;
int cycleCount = floor(frameOffset/@life);
@age = ((frameOffset/@life) - cycleCount ) * @life * @Timeinc
```

となります。

ここからiterationとcycleCountを利用して噴射ごとのidをclusterIdとでも名づけて作ります。
`int clusterId = iteration * 1000 + cycleCount;`
この値はノイズの中でオフセットさせる事でそれぞれの噴射のバリエーションとなります。

最終結果とコードはこのようになります。
![e12rz-lsx5m.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/748e219a-4056-f71e-f1ba-6d1d75944ce0.gif)

```
int iteration = @ptnum%4;
float frameOffset = @Frame - iteration*30;
@life = 120;
int cycleCount = floor(frameOffset/@life);
@age = ((frameOffset/@life) - cycleCount ) * @life * @Timeinc;
int clusterId = iteration * 1000 + cycleCount;

vector g = set(0,-9.8,0);

float dist = length(@P);
vector vDir = set(0,1,0) + vector( onoise(@P*0.5 + set(0,rand(@ptnum) * 4,0) + clusterId ,5,0.5,1))*1.25;
float vSpeed = fit( onoise(@P*2,5,0.5,1),-1,0.4,0,1 ) * fit01(pow( rand(@ptnum) ,0.5),0.5,1) * 15;
@v = vDir * vSpeed;

vector particle(vector v, gravity; float age){
    return v * age + gravity*0.5*pow(age,2);
}

vector rest = @P + particle(@v, g, 32.0/24);
float noise =  abs( onoise(rest*set(1,0.75,1)*0.25 + clusterId + 11 ,3,0.4,1 ) );
if(noise>0.005)removepoint(0,@ptnum);

float colorNoise = abs( onoise(rest*set(1,0.75,1)*0.25 + clusterId + 100 ,3,0.4,1 ) );
@Cd = chramp("color",fit(@age,0, fit(colorNoise,0.4,0,0.5,15), 0, 1 ));

@P = @P + particle(@v, g, @age);
```

作ってみたファイルはこちらになります。
https://drive.google.com/file/d/1qkTtOUPmeIx9q5Tcyn5Q6OX3EtbjBsj9/view?usp=sharing

この結果ではマグマが30フレームの一定感覚で噴出していますが、30フレーム分の余白を持たせているので　iterationとcycleCoutを利用し、ageを再構築して噴出のタイミングをもう少しランダムにさせても良いかもしれません。


では以上になります。ここまで読んでいただきありがとうございます！