【blender】Geometry Nodesでカーブに沿ってメッシュを変形

# やりたいこと
任意のメッシュを任意のカーブに沿って変形させたい。
まっすぐなヘビのモデルをカーブで手軽に変形させたり、つたなどの細長いオブジェクトのジェネレータへの応用が期待されます。

# 前提知識
- Geometry Nodesの基本的な概念
- 各ノードの意味
- 高校数学(特にベクトル)
以上

# アイデア
変形したいメッシュはもともとx軸に沿っているという前提で考えます。
メッシュをyz平面で輪切りにするようなイメージで細分化し、それぞれの平面をカーブの対応する点に再配置します。
![-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/c39b8d1e-655c-a175-38e4-dc8b4762782a.png)


# 流れ
- 変形させるメッシュの準備
- カーブをサンプリング
- メッシュをサンプリング
- 頂点の再配置
    - メッシュの複製
    - 位置を変更
    - タンジェントとノーマルを適用
   

# 変形させるメッシュの準備
変形させたいメッシュを準備します。
カーブに沿って変形させるため、十分に細分化されていないと変形後にガタガタします。
今回は先細らせた三角柱を蛇の胴体に見立てて使用します。
![0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/fdd46e40-6cc3-3a04-ca4c-19108808297f.png)

# カーブをサンプリング
入力として渡ってきたカーブの情報をサンプリングします。
カーブはfactorという値を保持しており、それぞれのストロークについて始点が0、終点が1です。
下図カーブ上の点にfactorに応じたスケールのインスタンスを配置することで、その値を可視化しています。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/4f4dc699-f7f7-652c-83f1-81314347bb17.png)

:::note
よくあるカーブ上にものを配置する系のチュートリアルはここまでの内容で作れます。
factorではなくlengthを使い、一定の間隔でサンプリングを行いインスタンスを配置します。
:::

次にカーブ上の任意のfactor値の点を**sample curveノード**を使い、直接サンプリングしてカーブのタンジェントを取得し、カーブに沿って回転させます。ここまでで、ソースのカーブから情報を取得することができるようになりました。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/64ffbf9d-bfe9-2f59-b5c1-db64ac6ee9e7.png)

:::note warn
blender3.4より前のバージョンではsample curveノードのcurve indexピンがありません。
複数のカーブがある状態でサンプリングをすると、すべてのカーブが対象になるので、めちゃくちゃな値が返ってきます。
:::

# メッシュをサンプリング
x軸に沿ったオリジナルのメッシュの各頂点について、それらがカーブのどの位置に対応するか、つまりfactor値を求めます。
これはとても簡単で、下の式で求まります。

```math
factor(x) = \frac{x - x_{min} }{ (x_{max} - x_{min})}
```

可視化したとき、きちんと0から1へグラデーションがかかっているのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/20480ab4-1f1e-35f5-ccb3-233403b6c81a.png)

# 頂点の再配置
### メッシュの複製
カーブが複数ある場合、その数だけメッシュを用意する必要があるので、カーブの始点にインスタンスを作成します。
このとき、各インスタンスに紐づくカーブのインデックスをattributeに保存します。
後々の処理を考えて、生成位置を原点にするために一度各カーブの始点をサンプリングしています。
(ここをinstanceのpositionで求めようとすると接続時にエラーになりました。わかる方コメントをいただけますと幸いです。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/944478b7-a016-a671-f30f-7ef73ec40d9c.png)
この段階で最終的な頂点が生成されたので、各頂点のfactorを求めカーブのサンプリングを行い、attributeに保存しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/4204838c-849b-007e-c3f9-b01e78d52146.png)

### 位置を変更
まずは各頂点の位置を変更します。
上で求めた各頂点のfactorで対応するカーブをサンプリングし、頂点が存在するyz平面の原点をその位置に持ってくるという処理です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/7571d295-7f6b-a507-8ecd-0811965196ff.png)

### タンジェントとノーマルを適用
タンジェントはカーブの微分値です。
つまり任意の点でカーブがどの方向に延びているかということです。
ノーマルはカーブのねじれの方向です。
デフォルトがどの方向かはわかりませんが、タンジェントとは直行しています。(おそらくデフォルトは-y軸方向ではないかと思います。)
これらを使い、頂点のyz平面を一意に回転させます。

最初に説明した通りオリジナルのメッシュはx軸に沿って配置されているという前提です。
よってカーブのタンジェントはyz平面のノーマル方向、カーブのノーマルはyz平面のタンジェント方向です。
ややこしいですが、図を見てみるとわかります。
そしてyz平面のノーマル方向は当然直行するx軸方向、タンジェント方向は平行なyz平面上のベクトルとなります。ここでは-y軸方向をyz平面のタンジェントとしてみます。

回転には**Align Euler to Vectorノード**を使用します。まずx軸をタンジェントに一致させます。
その後-y軸をノーマル方向に一致させます。
タンジェントとノーマル、x軸と-y軸はそれぞれ直行していますので、完全に一致させることができます。
ただし-y軸の回転の際にはローカル座標系で行う必要があるので、ノードを2連結させて一意に回転を定めます。
さらにこれらはyz平面を回転させていたので、x=0である必要があります。
よってyz平面の回転はyz平面の移動の前に行います。

最終的なノードはこちらになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3179083/928c55fb-5b28-0293-1709-750f5124a337.png)

# あとがき
備忘録のようなものでしたので粗末な文章ですが、お役に立てましたら幸いです。
Geometry Nodeを学び始めて1週間程度ですが、とても面白くmodifier等よりも細かな調整が可能だなという印象を受けました。
今後プロシージャルなアプローチはさらに需要が高くなるでしょうから、勉強しておくのが吉だと思います。
間違った箇所等ございましたらコメントにてご教授ください。
ここまでご覧いただきありがとうございました。

# 参考になるサイト、動画
https://www.ultra-noob.com/blog/2022/58/

https://www.youtube.com/watch?v=PbzlyubfGbQ