Niagaraの数式で行えるIF文 “Lerp”(+Sin・Cos)

#基礎編
### IF文とLerpとは
例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/365d4a6b-86f3-6293-4296-2697ae41ca91.png)
(中央からの距離に比例してサイズを変えたもの)

>**◆IF文とは**
…もし●●なら■■する。（そうでないなら▲▲する）

>**◆Lerpとは**
…入力された値が0なら■■、1なら▲▲(0~1の間はブレンド)

→Lerp の入力された値を制御することでIF文として使える！
　(入力された値●●が0なら■■する。●●が1なら▲▲する)

<br>

### NiagaraでIF文を作る

ここでは、パーティクルの位置をノーマライズ(0~1に変換)し Lerp に入力
「0ならパーティクルのサイズを2、1ならパーティクルのサイズを20にする」
という処理を行っています

この処理を Niagara 上で表示したときには次の画像のようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/f98f127e-4096-2bc8-b387-6e584ea2d7f5.png)
上から順番に説明していきます

>**◆Vector 2DFrom Float**
…パーティクルのサイズはX軸Y軸の2軸(Vector 2D)で決まっているので、
両方に同じ数値を入れます という処理

>**◆Lerp Float**
…入力された値●●が0なら■■する。●●が1なら▲▲する
Aには0の時の値(■■)、Bには1の時の値(▲▲)を入れる
Alpha には入力する値●●を入れる

>**◆Normalize Float**
…設定された Max , Min の数字に応じて Value の値を正規化する
この例の場合は Max が200、Min が0なので Value が200の時に1 (400の時は2) になる

>**◆NDIS Abs**
NDIS Abs の中身はこのようになっています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/cbde9821-37af-b1dc-1fda-827a0b9ebc32.png)
絶対値を取るだけの Niagara Dynamic Input Script です

>**◆Subtract Float**
…引き算です

>**◆Make Float From Vector**
…Vector に入力された値から Channel でX,Y,Zのいずれかを選びその数値を使います
Vector に(10,15,20)という値が入っている時に Channel で Z を選ぶと20になります
ここでは Particle Position から原点(Simulation Position)の値を引いて距離を取っています


**まとめると**
原点からパーティクルの位置を取ってから絶対値を取って正規化
それを Lerp の条件として使用し、距離に応じてサイズを変更

<br>

ここで Niagara に使い慣れている人は
**「いちいち Lerp 使わずに距離だけをとって足し算掛け算したらいいのでは？」**
と思うかもしれません

この例においてはその通りです
なので応用編では Lerp の IF 文を使わないとできない例を出して解説します

#応用編

##用意

鳥が**「一定以上の速度で上方向に向かって飛んでいる場合にのみ羽ばたかせたい」**
応用編ではこの例を用いて説明していきます

<br>

まずはモデルを用意します
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/ecee8216-851f-f8be-ae56-6a3a2653232f.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/3f60e5d2-0465-8cfe-c60b-2260cd08ad05.png)

どこからどう見ても鳥ですね　UV も適当に開いておきます
![T_BirdWing_Math_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/38f0b155-45b2-722b-0ce2-64c25ef8ac60.png)
羽ばたかせる用のテクスチャも作っておきましょう

World Position Offset で羽ばたかせるためのマテリアルを用意します
マテリアル内ではベクトル計算についての知識が必要ですが、
ここでは説明せずに Niagara 内で計算する際に説明します

<br>

こちらが今回のマテリアルになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/9c4b052a-c8f6-4354-eaae-a5978a241041.png)
最低限の構成なので簡単に作れると思います

##羽ばたかせる
###Niagaraの設定

ここから Niagara の作業に入っていきます

<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/aa105cbe-53ab-2694-1357-3a10c7166c17.png)
モデルにマテリアルを適用しスポーンさせてランダム方向に飛ばします
Facing Mode はとりあえず Velocity にしておいてください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/85ab3f95-74b3-4aff-bfdb-8835132daa1e.png)
Dynamic Paramatersの設定を行っていきます

各パラメーターの説明を行います

>**◆WingFlap**
...0～1で往復させることで羽ばたくようになっています

>**◆AgreeX,Y,Z**
...今向いてる方向に対してそれぞれの方向にどれだけ羽ばたくかというパラメーターです
画像では0となっていますが、**1を入れておいてください**

>**◆WPOStrength**
...羽ばたく大きさです
モデルの大きさに合わせて適当な数値を入れておいてください

<br>

###羽ばたきを考える

ではまず**WingFlap**から調整をしていきます

羽ばたくときは0~1を往復してほしいのでまずSinを使います
羽ばたかないときは羽を水平に保ってほしいので、WingFlap に0.5が入っていてほしい・・・

**そうですね、Lerp ですね**

今回は羽ばたく条件として
・上向きの速度が40以上の時に羽ばたく
とします

<br>

###Velocityに応じて羽ばたかせる

Niagara で実際に組むとこのようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/10696029-81d7-1542-03cf-4e2bf0592f15.png)
軽く説明すると、Z軸のベクトルの最小を39.99...・最大を40として正規化
それを0～1で Clamp することで39.99...未満は0・40以上は1になります。
(Max と Min に同じ数字を入れると、ちょうどその数字が出た時に NaN になってしまうので少しずらします)
それを Alpha に入れて Lerp の AB を設定することで Z 方向に40以上で飛んでいる場合にのみ羽ばたくようにできます

<br>

###成分毎に分解して羽ばたかせる

次に**AgreeXYZ**を計算していきます
**さっきより かなり 面倒**ですが、ここを理解できれば Cos・内積はある程度扱えるようになります

Cos というのは円を描いた時の中心からの位置をX軸Y軸に分けたものの片側です
内積は2つのベクトルからなる角の Cos を計算することができます
Sin・Cos については下記のサイトの gif を見ていただければ理解できると思います
https://qiita.com/rakusan/items/2f60f2ae6af208dda40f

<br>

それではまずは X から考えていきます

羽が X 方向に移動してほしいときというのは、**鳥が Z 軸方向を向いている時**かつ**鳥が X 軸方向も向いている時**になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/bf4b358c-f263-bb58-c040-52f9eb70b516.png)
(一番左は縦方向を向いていないので、横軸方向には羽ばたかない)

Z軸方向に Velocity がかかっている・X軸方向に Velocity がかかっている
という2つの条件があっても Lerp で対応できます

A には0を入れておき、B で X 軸方向の条件を、Alpha で Z 軸方向の条件を満たしていきます

<br>

まず B については Niagara 内ではこのようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/5f830d2b-0399-8bc8-f806-9e2f07646718.png)
順番に説明していきます

>**◆Dot Product**
...内積の計算を行ってくれます
A と B にベクトルを入れます
ここでは A に Velocity、B には X 軸を反転させたものを入れています
X 軸を反転させる理由ですが、後程説明します

>**◆Divid VectorとCector from FloatとVector Length**
Dot にそのまま Velocity を入れると1以上の数値が出てしまいます
そこで
(**a**・**b**)/(|**a**||**b**|)=Cosθ
このような式に変換してベクトルの大きさを正規化することで Cosθ（-1～1）を出すことができます
※式の意味は理解しなくても大丈夫です 2ベクトルから-1~1の値を出すことができるとだけ覚えておいてください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/b67b965c-d77d-dbd9-9540-cd89fe8812f4.png)
向いている方向によって何かをしたいって時はよく使うので NDIS 化しておいてもいいかもしれません

...ちなみに今回の場合は(**a**・**b**)/(|**a**||**b**|)の式において、片方のベクトルは単位ベクトル(大きさが1)でX成分にのみ数値が入っているため、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/081302cd-cacf-de43-38fb-5e1aa1f123e2.png)
このように書き換えることもできます

<br>

次に Alpha はこのようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/03782c10-7bbf-021c-b6f3-bbb4fc195d58.png)
上の B で行った内積の簡略化した式を使っています
今回は上方向なので Channel が Z になっています
(Alphaには-1~1の値が入っていますが、動きには問題ないので無視しています)

これで上方向を向いているかつ X 軸方向を向いている時に羽ばたくという設定ができました

<br>

AgreeY も同じように設定していきましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/6e9778ee-3ced-8434-bd3c-c1fb21a1f8f2.png)
Channel が X だったところを Y に変更しただけになります

<br>

そして AgreeZ です
これはとても簡単です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/11b00926-6c08-0521-5a48-fc43353e4a22.png)
Z 軸を向いている時に羽ばたかなければいいだけです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/1a93c828-4552-860f-d3a6-129111ab6d15.png)
Z 軸方向を向いていれば羽ばたくという処理を One Minus を通すことで
Z 軸を向いていれば羽ばたかないという処理に変更してるだけです

<br>

**AgreeXY で-1をかけていた理由**
これは Sin・Cos の性質で
Sin は0~π(180度)の時に正・π~2πの時に負
Cos は-π/2~π/2の時に正・π/2~3π/2の時に負
となります
そのため-1をかけておかないと一定方向に動いた際に(鳥にとっての)縦ではなく横に羽ばたいてしまいます
試してみてください

<br>

##回転を考える

まだおかしいところがありますね
移動方向に対して回転している場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/ffd5ec5e-176e-ce20-ba2c-335f143b2d9e.png)
このようにオフセット方向が対応しきれていません

解決方法は2つあります
>1.Velocity に合わせてメッシュを回転させて正しい方向を向かせる
>2.回転方向も考慮して羽ばたく方向を変える

簡単なのは1の方...というよりも2はかなり面倒なので1を採用します

<br>

###クォータニオンを変換する

Roll Pitch Yaw でいうと Roll に回転が入っていなければ正しく羽ばたいてくれそうです
そこで**Mesh Orientation**というモジュールを使います

このモジュールは**クォータニオン**という4つの数字で回転を制御しています
クォータニオンが何かを簡単に説明すると、
「回転する軸を設定・回転量を設定」って感じです

<br>

クォータニオンのまま制御するよりも Roll Pitch Yaw に変換したほうが制御が簡単なので、変換する NDIS を作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/f9c162d3-fca9-ba96-5ccc-2c35fab65220.png)
これだけです
X が Roll、YがPitch、ZがYaw として扱えるようになります
(このモデルはX軸方向を前として作成しているため)

Facing Mode が Velocity のまま回転させると中指を立てたくなる挙動をするので、
Default に戻しておきます

<br>

###回転させる

今回は Roll が回転しなければ綺麗にいきそうなので、Xには0を入れておきます

初めに**Pitch**を設定していきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/3a673758-cf8e-0e9b-d9ed-28c05cd20c71.png)
必要そうなものだけ説明していきます

>**◆NDIS Vector to Quat**
...先ほど作った NDIS で Vector4 を Vector に変換します

>**◆Break Vector**
...Roll Pitch Yaw でそれぞれ計算したいので、Vector を3つの Float に分解しています

>**◆Normlized float**
...Cos は-1~1の値を取るので、-1を0、1を1として扱うように正規化しています

後は内積を簡略化させたものです
Lerp の AB の値が-0.25~0.25なのは Cos が0の時に正面を向くからです

<br>

次に**Yaw**を設定していきます
Yaw の設定は X 軸との内積だけではなく、Y 軸方向に対して正負の判断が必要になります
そのため、数値を出すための条件が増えます
>Y 方向の Velocity が正で X 方向の Velocity が正であれば0
>Y 方向の Velocity が負で X 方向の Velocity が正であれば0
>Y 方向の Velocity が正で X 方向の Velocity が負であれば0.5
>Y 方向の Velocity が負で X 方向の Velocity が負であれば-0.5

となります
(1で一周するため0.5と-0.5は同じ方向を向きますが、回転方向が違います)

Niagara 上ではこのようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/b05ab22b-add0-4848-e62f-4ebbc874f8df.png)
ここで AB の値が ±0.5~0 ではなく ±0.25~0 である理由ですが、
AB の Alpha の中の計算は Cos を求めているので -1~1 の値を取ります
なので Alpha が1の時は0、0の時は±0.25、-1の時に±0.5となるようにしています
Pitch の時と同じように-1~1で Normalize すれば±0.5と書くことができます ~~が面倒なのでやりませんでした~~

Alpha については、Clamp&Normalize をすることで
0以上で1・-0.0001以下で0となるようにして疑似的に正負を取っています

<br>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/44c60fa7-64aa-623d-115b-c60f43060998.png)
上から見るときれいに移動方向を向いていることがわかりますね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553932/d9507d5f-a90c-f029-f13a-60b8c0e516ea.png)
横から見ても、しっかりと移動方向を向いてくれています

<br>

###蛇足

Roll に回転が入るのは基本的に鳥がカーブする時です
内側に背が来るように回転します
上から見た時に時計周りの場合に0~0.25、反時計周りの場合に0~-0.25の数値が入れば
今以上にきれいに飛んでいるように見えます

しかし Roll に回転が入っている場合、その回転量を鳥の羽ばたく向きに対応させなければなりません
先ほど「かなり面倒」と言った計算方法ですね

簡単に説明すると...
>・1F 前の Velocty と今の Velocity から加速度を取得できるNDISを作成
>・その加速度から回転方向を取得
>・向きによってLerpで回転させる → Float で参照できるようにしておく
>・回転量から鳥の背中の方向を取得し Z 方向との Cos を出す
>・Cos を考慮して AgreeXYZ にさらに Lerp
といった具合になります

現状(4.24.1)加速度を取得できないと思います
なのでどうしても Roll 回転させたい場合は、鳥の動きを Sin Cos なので制御して、
その周期もしくは回転方向に合わせて Roll を回転させてやる、というのが現実的です

Epic さん、Niagara 内で Acceleration を取得できるようにしてください
Particles.Acceleration という Vector を用意してください

<br>

#最後に

斬撃や爆発や魔法といった普通のエフェクトではこんな面倒な計算をする必要はないと思います

しかし「速度や大きさや寿命ごとに何かをしたい」といった少し特殊な表現を行う場合には
今回の Lerp を使うことで表現できる場合があるかもしれません

また Sin Cos は初めは取っつきにくいかもしれませんが、
実際には汎用性の高い素晴らしい関数です

ベクトル計算も理解するまでは「？？？」かもしれませんが、
方向・速度・場所などの Vector を用いて表現する場合には必須になってきます

ここまでついてこられた方は、Lerp の使い方・内積についてはある程度理解できたのではないかと思います

この記事が少しでも皆様のお役に立てたのであれば幸いです

この記事は以上です
お疲れ様でした

<br>

それにしても、こんな難しい例じゃなくて
もうちょっと簡単なものはなかったんでしょうかね(他人事)

書き始めた時は数学初心者向けに書こうと思ってたんですけど、
数学初心者さんはここまで残ってるのでしょうか・・・
残ってなかったらごめんなさい
残ってても理解できなかった人がいらっしゃいましたらごめんなさい
Twitter などでいつでも質問してくださって構いません
また間違いなどありましたらそちらもお願い致します