📝

VFXGraphのSampleDataを見ていく:Orient編

に公開

はじめに

公式の解説動画(https://youtu.be/DKVdg8DsIVY?si=gtGdQXlly9FBJB2O

Orient Face Camera


このエフェクトの肝は表題の通りOrientFaceCameraなんだと思うんですが、それ以外の技術が気になりすぎてしょうがないエフェクト。

共通して使うパラメーターはpropertyで管理する


ここではTransformプロパティを用意して、オブジェクトの発生位置として随所で使いまわしている
VFXGraphでは1つのグラフの中で複数のエフェクトを管理していくのでこれをやるやらないでだいぶ効率も複雑さも変わってくる

Eyes Face Camera-Initialize Particle

SampleMesh

https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@11.0/manual/Operator-SampleMesh.html
粒子を「メッシュの形に沿って配置する」「メッシュの情報を粒子属性にバインドする」ときに使う
「任意のメッシュ(ここではEYE_Monster)を入力すると、その 三角形の位置 / 法線 / UV / 色 / インデックス などを取れるノード」とNoteに書いてある

ここではEYE_MONSTERというモデルにSubMeshが適用されている
Element0にあたるEyesが優先的に選択される(はず)
SingleBurstが100なので、SubMesh0のそれぞれのTriangle100個に目玉の発生位置として設定される仕組み

SubMeshとは

モデルに適用されているマテリアルごとに自動的に分けられる

Modeについて
  1. Wrap
    三角形数で 割った余り に変換して使う。
    例えばメッシュに三角形が 100 個あって、Triangle = 105 のとき → 105 % 100 = 5 になる。
    つまりインデックスがループする感じ。
    →粒子数が三角形数より多いとき に便利。

  2. Clamp
    範囲外になったら 最後の三角形を使う。
    例えば Triangle = 150 でも、メッシュに三角形が 100 個しかなければ、Triangle = 99 に固定される。
    →外れ値を許容したくないときに安全。

  3. Mirror
    範囲外になったら インデックスを折り返す。
    例えば三角形数が 100 の場合
    Triangle = 101 → 98
    Triangle = 102 → 97

つまり「行って帰ってくる」感じで反射する。
→ループより自然な周期性が欲しいときに使える。

SurfaceCoordinatesについて
  1. Uniform
    三角形の面積に比例してランダムサンプリングする。
    大きい三角形ほど選ばれる確率が高くなるので、結果として メッシュ全体で均一な分布になる。
    →「粒子を表面全体にバラ撒きたい」ときに使う。

  2. Barycentric
    各三角形の内部を barycentric 座標でサンプリングする。
    つまり「三角形が選ばれたあと、その中で均等に配置される」。
    ただし、この場合 大きい三角形も小さい三角形も同じ確率で選ばれる から、メッシュ全体だと大きい面がスカスカ、小さい面が密集しやすくなる。
    →「各三角形に同じ確率で粒子を置きたい」場合に便利。

TexCoord.x(UV.x) → Size 入力へ
EYE_MONSTERのUVに埋め込んでおいた値を粒子のサイズとして利用している

EYE_MONSTERの三角形のUVSet2を確認すると、U座標にそれぞれ数値が入っていることが分かる

0.35くらい 0.18くらい

Eyes Face Camera-Update Particle

なんでRotate 3Dの値をSetPositionに?となったが、
目玉はずっとこっちを向く球なので、位置だけを動かしてるんですね
これはOrientFaceCameraらしいところかもしれない

Eyes Face Camera-Output Particle


ここでも注目するのはOrientではなく、ノイズの作り方
Noise1DCoordinateに値を流すとノイズが生成される仕組みだが、
TotalVFXTimeで毎フレーム変化を付ける
RandomFloatでパーティクルごとにズレを作る
このようなノイズ生成は今後も頻繁に使用するシステム

RandomFloatノードの設定に注意で、SeedがPerParticleなのでパーティクルごとに設定され、Constantにチェックが付くことで最初の一回だけ生成される。
NoiseのRangeは0~1にしておいて、各項目用に後ろでLerpする時の補完係数として利用している

Body-UpdateParticle


このAxisX/Y/Zの取り決めが個人的に大変悩まされた部分

Rotate3Dノードの理解

このあたり、「ベクトルは座標である」という概念を以下の3Blue1BrownJapanさんの動画シリーズで学んでいたおかげでそこまで躓かずに進めた。
https://youtube.com/playlist?list=PL5WufEA7WHQGX7Su06JzbPDXUQGOd0wlq&si=gnYev4rGMEEHTzYQ

【入力】
Position … 回したいベクトル
Rotation … 軸
Angle … 回転の量。ラジアン値(πとか)
Rotation Center … 軸の中心位置、らしいが現状使い道が分からない
【出力】
Position … 回転後のベクトル

段階

1.回転させたい方向ベクトルの入力
2.回転軸の指定
3.角度を与える
4.出力ベクトルの利用

解説

1.サンプルで回転させたいのはYベクトル(0,1,0)
2.Xを軸(1,0,0)に回転させたい
 ここでは-1。右手座標左手座標というやつに影響を受けている
3.なんで悩まされたかというと回転中、Z座標も変動することを失念していたこと
 例えば45度回転したとき、(0,0.7,0.7)
 Zベクトルを規定しないと「どこにいけばいいの?」とモデルが破綻する
 なので、Zベクトルも規定してあげる必要がある
4.Zベクトルは回転軸であるXベクトルと、回転中のYベクトルの外積によって導き出せる
 なのでスクショのようなノード構成となる

例:首を振りつづける場合



1.PositionでZ軸をセットし(0,0,1)
2.回転軸RotationAxisをY軸にセットする(0,1,0)
3.出力されるベクトルは回転中のZ軸なので、そのZ軸と固定されたY軸(0,-1,0)の外積を取ることで、
残り1つの軸であるX軸を求める。
4.VFXTimeをSineに繋ぎ、Angleにつなぐ

Orient Fixed Axis

Eyes Fixed Axis-Initialize Particle

いよいよ本格的にカスタムアトリビュートが使用されてくる感じ。
SampleMeshから出力されるPositionや法線方向、UVに格納されているスケールを保持しておく。

Eyes Fixed Axis-Update Particle

Look At

ある点から別の点を向かせるための方向ベクトルを作成する
Initializeで手に入れて置いたInitPos、InitDirectionをここで使う

入力名 内容
From どこから見るか
To どこを見るか
Up どこを「上」とするか

Fromにはパーティクルの生成される座標(InitPos)、ToにはTransform(オブジェクトの中心)、Upには法線方向を繋げている。
中心への向きだけで安定しそうだが、法線方向を「上」と設定しなければ、自由にひっくり返る余地を与えてしまうことになる。

Transform(Matrix)


変換行列を作るノード。これもまさに線形代数のエッセンスのおかげで理解できた。
ここでは、
1.LookAtで基準となる座標系を作成
2.Transform(matrix)が事前に作られた変換行列に従ってその座標を変換する

個別に「回転」「移動」等を足すよりも、行列一回の掛け算の方が処理も楽らしい

LookAtで粒子ごとの基準の姿勢を作り、
Transform(Matrix)でモデルの表面に移動。
SetOpenは100個単位(Modulo 5000%100)でバラバラに設定しているから
サンプルデータのような100個が順番に花開くような見た目を作れる

Open Animation

Update内で'Open'というFloat型のカスタムアトリビュートを作成し、
さらにこのアトリビュートのデータをいじって'PositionShapeCircle'のRadiusに使用することで、花が開く動きを付けている
これはすごい

Eyes Fixed Axis-Output Particle

Orient: Fixed Axis


Upを指定してあげるOrientノード
Normalを接続すれば、Normal方向に向いてくれる
それの応用版がこのノード構成

Note翻訳 AxisDirection
initial direction 属性は目のソケットから外に向かう法線で、Direction 属性は update context 内の Position arc circle block によって設定され、円の法線に対応する。orient block に軸を与えるため、initial direction と direction attribute の間を lerp(補間)しており、その補間には Flower Animation の open 属性を使用している。

Orient Advanced

Output Particle

Orient Advanced

サンプルではZYを指定する

Note翻訳 Orient Advanced
アドバンストモードでは、orient block は2つのベクトルを必要とする。Z軸は正面ベクトル、Y軸は整列ベクトルとなる。ここではパーティクルを常に球の法線方向に向けたいので、Z軸はパーティクル位置−球の中心で得られる法線とする。Y軸の整列ベクトルは速度ベクトルを使い、粒子が進行方向に揃うようにする。また、ベクトルを正規化する必要はなく、Orient Block が自動で正規化処理を行う。

私の感覚ではZって奥行じゃないの?とか座標系イメージがぐちゃぐちゃになってしまうんですが、
・Unity の Quad の表面法線は ローカル Z+
ローカル Y は縦方向(アップベクトル)
この原理だけ覚えたらすっきりしました。

まとめ

Orientが主題なだけあって、かなり座標軸に振り回される内容だった。
おかげでふわふわしていたエフェクト作成時の座標の扱いが見直せた。素晴らしい。

Discussion