Skip to content
第14回 メッシュ表面からのパーティクル生成

4 / 4 Skinned Meshの変形に追従する ​

このステップの目的

生成時に選んだ表面の場所を保存し、位置と法線を毎フレーム取得して、毛をモデルの変形へ追従させます。

比較サンプルを確認する ​

Skinned Meshは、ボーンの位置や回転に応じて頂点が変形するメッシュです。
モデル全体をTransformで回す場合と異なり、足や胴体などの部分ごとに形が変わります。
第14回のウサギは静的なMeshなので、このページでは歩行アニメーションを持つChomperを使います。

Playを停止し、第14回のPackageに含まれるScenes/SkinnedSurfaceFur.unityを開いてください。
左のInitialize Onlyは生成時だけ位置と法線を取得し、右のUpdate Followingは生成後も取得を続けます。
本数・長さ・太さとSeedをそろえ、位置を取得する時点による違いを比較する構成です。

左は生成時の位置と向きを保った毛、右は変形後の表面から位置と法線を取得し続ける毛。歩行中のChomperを同じ条件で比較左は生成時の位置と向きを保った毛、右は変形後の表面から位置と法線を取得し続ける毛。歩行中のChomperを同じ条件で比較

Playし、足や胴体の動きを観察しましょう。
左の毛は生成時の場所に残ったままです。変形する表面との間にずれが生じます。
右では根元が表面と一緒に動き、毛の向きも変わります。
EditorのPauseとフレーム送りを使うと、小さなずれを確認しやすくなります。

実習ではSceneをSkinnedSurfaceFurPractice.unityとして複製します。
GraphのSkinnedSurfaceInitialize.vfxもSkinnedSurfacePractice.vfxへ複製し、左のモデルのBodyにあるVisual Effectへ割り当ててください。
右の完成見本は比較用に残します。

SkinnedMeshRendererをGraphへ渡す ​

静的なMeshでは、BlackboardのMesh型プロパティへMeshアセットを指定しました。
変形中の形を読むには、Scene上のSkinnedMeshRendererを参照します。
サンプルのSourceSkinnedMeshは、外部公開したSkinned Mesh Renderer型のプロパティです。

Hierarchyで左のモデルのBodyを選びます。
Visual EffectのSourceSkinnedMeshに、同じBodyのSkinnedMeshRendererが指定されていることを確認してください。
Graphを差し替えた後に参照が空になっていたら、BodyのSkinnedMeshRendererを指定し直します。

Graphでは、Sample MeshのSourceをSkinned Mesh Rendererにすると、Sample Skinned Meshとして扱えます。
見本はPlacement ModeをSurface、Surface CoordinatesをUniformとし、PositionとNormalを取得しています。
三角形の数は、同じ参照元のGet Skinned Mesh Triangle Countで取得してください。
Sample Meshの設定項目を参照してください。

サンプルではVisual EffectとSkinnedMeshRendererを同じGameObjectに配置し、パーティクルをLocal空間で扱います。
Skinned TransformはApply Local Root Transformです。
変形後の表面と毛の座標空間をそろえるため、実習中はこの設定を保ちます。

モデルのRead/Writeは有効にしてあります。
SkinnedMeshRendererのUpdate When Offscreenも有効にし、画面外でもサンプリング元の頂点を更新する設定です。

生成場所を保存する ​

同じ毛を同じ表面の場所へ追従させるには、どの三角形のどの位置から生えたかを保持します。
毎フレーム違う乱数で場所を選ぶと、毛の根元が表面を移動してしまいます。

配布GraphのBlackboardには、次のCustom Attributeを用意しています。
Custom Attributeは、パーティクルごとの保存領域です。
外部からGraph全体へ値を渡すプロパティとは区別して使います。

Custom Attribute型保存する情報
triangleIndexuint生成時に選んだ三角形の番号
surfaceCoordVector2選んだ三角形の内部で位置を求める二つの値

Initializeの先頭で、UInt型のRandomをtriangleIndexへ、Vector2型のRandomをsurfaceCoordへ保存します。
保存した値をGet Attributeで読み、Sample Skinned MeshのTriangleとSquareへの接続を確認してください。
三角形の選び方とSquareの意味は、14-2「Mesh の表面からパーティクルを生成」と共通です。

図を準備しています…

保存するのは、表面の場所を指定する情報です。
変形前のPositionだけを保存しても、ボーンが動いた後の位置は分かりません。
同じ三角形と面内座標から、その時点の位置を求め直します。

位置と法線をUpdateで取得する ​

複製したSkinnedSurfacePractice.vfxを開いてください。
Initializeには表面のPositionを使うSet Positionと、Normalを使うSet AxisYがあります。
Updateへ次の二つのBlockを追加します。

UpdateのBlock接続元更新するもの
Set PositionSample Skinned MeshのPosition毛の根元
Set AxisYNormalを受け取るSafe Normalizeの出力毛の伸びる向き

まずSet Positionだけを追加し、Initializeでも使っているSample Skinned MeshのPositionを接続してください。
Playすると根元は表面に追従しますが、毛の向きは生成時のままです。
次にSet AxisYを追加し、既存のSafe Normalizeの出力を接続します。
Safe Normalizeでは法線の長さをそろえ、向きとして使います。

一つのSample Skinned Meshの出力を、InitializeとUpdateの両方へ接続できます。
同じノードでも、Initializeから使う処理は生成時に、Updateから使う処理は毎フレーム実行されます。
Updateでは新しい乱数を作らず、保存したtriangleIndexとsurfaceCoordを再利用してください。

毛のMesh、Scale、OutputのOrientは14-2と共通です。
位置のIntegration、寿命による消滅など、毛を固定するUpdateの設定も維持します。
表面の位置を直接設定するので、速度やForceによる移動を重ねる必要はありません。

追従を確かめる ​

練習用SceneをPlayし、左のモデルと右の完成見本を比較してください。
足が曲がる場面や、胴体が上下する場面でも、毛の根元と向きが表面に沿うかを確認します。

次に、UpdateのSet Positionだけを無効にした場合を予想しましょう。
Playを停止してBlockを無効にし、再度Playして表示を確かめてください。
根元は生成時の場所に残るため、動く表面との間にずれが生じます。
Playを停止してSet Positionを有効に戻し、今度はSet AxisYだけを無効にして再生します。
根元が動いても、毛の向きは更新されません。
比較後は両方のBlockを有効に戻してください。

この見本は、毛の根元と向きを表面へ追従させる構成です。
毛そのものの曲がりや、風による揺れは扱いません。
また、別のMeshへ差し替えると三角形の番号も変わるため、保存済みの場所をそのまま使わず、毛を再生成する必要があります。

チェックリスト
  • Meshアセットと、変形中のSkinnedMeshRendererの参照を区別できる。
  • 保存した三角形番号と面内座標を使って、同じ表面の場所を取得できる。
  • Updateで位置と法線を更新し、足や胴体の変形へ毛を追従させられる。