VEXスニペットの登録の方法と内積について

どうも、めんどくさがりまさおです。
はい、すべったー。
:sob:
HoudiniではVEX(Vector Expression)という言語でコードを書くことが多いのですが、ゼロからコードを書くのってめんどくさいですよね。
そんなめんどくさがりな人たちに役立つ情報を提供したいと思います。

今回は、VEXスニペットの登録の方法について紹介します。

##VEXスニペットを登録する方法

ところでスニペットって何？
**Snippet:断片**
ん？:ear_tone4:
スニペットは、コードを記述する時によく使うコードを雛形として定義したものです。:muscle_tone3:
なので、スニペットを使用することで、煩わしいコード入力が楽になります。
Wrangleノードで言うと、VEXpressionパラメータの隣にあるリストで表示されるものがスニペットであり、そこから何か選ぶことで、スニペットに登録されているコードがVEXpressionに入ります。
![WrangleNode.png](https://qiita-image-store.s3.amazonaws.com/0/154191/cd2c01db-a847-8399-314f-8c8b34e6ada4.png)
ここでは、既存のスニペットだけでなく、独自のスニペットを追加していきます。
そのために、環境設定について説明しておく必要があります。

Houdiniは、ノードという部品が提供されており、そのノードの中も筒抜けでカスタマイズすることができます。
それは環境設定でも同様です。
例えば、Houdiniのインストールディレクトリの"houdini"フォルダ(いわゆる$HHグローバル変数)の中身を見てみましょう。

- **houdiniフォルダ**
![HoudiniFolder.png](https://qiita-image-store.s3.amazonaws.com/0/154191/312d3184-f1a9-6e13-b4ba-391509df3381.png)
これがHoudiniの環境を構成しているリソース群です。
Houdiniを使用すると、ホームディレクトリ(Windowsならマイドキュメント)直下に"houdiniバージョン名"のユーザー初期設定フォルダが作成されていると思います。

- **ユーザー初期設定フォルダ**
![HomeFolder.png](https://qiita-image-store.s3.amazonaws.com/0/154191/e8ad226d-c0e5-ca18-28cc-69e956656461.png)
これらの中身を比較してみると、"config"とか"toolbar"とか同じフォルダが存在するぞ？
実は、Houdiniは、環境設定ファイルを読みに行く時に、ユーザー初期設定フォルダの情報を優先して読みに行き、そして"houdini"フォルダの情報を読みに行きます。
赤い枠で囲んだ部分が、注目すべきリソースです。

- **configフォルダ**
configフォルダには主にGUI関係のリソースがあり、例えば、その中の3DSceneColors.darkはEditメニューのColor Settingsで設定できるカラー情報が定義されたファイルです。
![ColorSettings.png](https://qiita-image-store.s3.amazonaws.com/0/154191/15879c65-3ae2-69dc-7af0-be72d22e9335.png)
そのファイルをユーザー初期設定フォルダ直下の"config"(なければ作成する)フォルダにコピーして、それをテキストエディタで編集することでカラーを編集することができます。

- **fontsフォルダ**
Houdiniで使用するフォントが格納されています。
ステッキーで日本語を書く時に、例えばMeiryoフォントを使いたいのであれば、この中のfont.mapをユーザー初期設定フォルダ直下の"fonts"(なければ作成する)フォルダにコピーして、それをテキストエディタで以下のように編集すれば

```text
Black			"Vegur Bold"
Proportional		"DejaVu Sans"
Proportional-CJK	"Meiryo"
Fixed			"DejaVu Sans Mono"
Fixed-CJK		"Meiryo"
```

この:strawberry:と見間違えちゃう"海":ocean:という文字
![ChinaFont.png](https://qiita-image-store.s3.amazonaws.com/0/154191/add20153-05dc-b121-1abe-999357e43a73.png)
が
![JapanFont.png](https://qiita-image-store.s3.amazonaws.com/0/154191/144785a2-32a9-a9d3-d860-b8202d37eb36.png)
になり、日本語が読みやすくなります。

- **otlsフォルダとscriptsフォルダ**
Houdiniで使用されているHDAアセットファイルとスクリプトが格納されています。
ユーザー独自で作成したHDAアセットは、ユーザー初期設定フォルダ直下の"otls"(なければ作成する)フォルダに、
同様にPythonスクリプト、HScriptは、"scripts"フォルダに配置します。

- **VEXpressions.txt**
Wrangleノードで読み込まれるスニペットが登録されています。

**このVEXpressions.txtが今回の本題です。**

Wrangleノードで選択することができるスニペットは、その"houdini"フォルダの直下にあるVEXpressions.txtに登録されているコードが読み込まれています。
ユーザー初期設定フォルダ直下に**VEXpressions.txt**という名前のテキストファイルを新規に作成し、そこにコードを記述することで、独自のスニペットを登録することができます。
まずは、"houdini"フォルダ直下のVEXpressions.txtをテキストエディタで開いて見てみます。
![VEXpressions.png](https://qiita-image-store.s3.amazonaws.com/0/154191/0b186f6c-fb74-8f25-2ac6-fda96b51e037.png)

VEXpressions.txtの書式では、#がコメント行になります。
スニペットを登録する書式は、

```text
Wrangleノード名/スニペットパラメータ名
[インデント]スニペットのコメント
[インデント]実際のコード
[インデント]...
```
です。
"Wrangleノード名/スニペットパラメータ名"は
*pointwrangle/snippet*
*attribwrangle/snippet*
*volumewrangle/snippet*
*popforce/localforceexpression*
などが該当します。
Houdini13の頃はpointwrangleとattribwrangleという名前のSOPが別々に存在していました。
***pointwrangle*はHoudini14から*attribwrangle*に統一されたのでHoudini14以降は使用しません。**
[インデント]は、半角スペース1文字以上またはタブ文字を使用することができます。
わかりやすいように、半角スペース4文字を使用しましょう。
スニペットには日本語を使用することができます！(ただし、VEXpressions.txtの文字コードを**UTF-8**で保存する必要あり)

では、VEXスニペットを登録してみましょう。
ユーザー初期設定フォルダ直下に**VEXpressions.txt**というテキストファイルを作成し、
テキストエディタで開き、以下のコードを入力してみましょう。

```text
attribwrangle/snippet
    ポイントを軸で捻る
    vector axis = chv("axis");
    vector center = chv("center");
    float rate = chf("rate");
    float distance = dot(@P - center, normalize(axis));
    matrix3 rotMatrix = ident();
    rotate(rotMatrix , rate*distance, axis);
    @P -= center;
    @P *= rotMatrix ;
    @P += center;
```

そして、文字コードをUTF-8で保存します。
そしてHoudiniを起動します。
適当に何かのジオメトリを作成し、Attribute Wrangle SOPを接続します。
そして、そのAttribute Wrangle SOPを選択し、"Run Over"が"Points"になっていることを確認します。
そして、VEXpressionの横にあるスニペットボタンをクリックすると、以下のように独自で定義したスニペットが追加されていることがわかります。
![CallSnippet.png](https://qiita-image-store.s3.amazonaws.com/0/154191/da004ed2-4843-1b66-2197-f6db7a27037e.png)
それを選択すると、このようにコードが入ります。
![InsertSnippet.png](https://qiita-image-store.s3.amazonaws.com/0/154191/2a6ad76b-9f2c-e433-5e16-15a7db2f09dc.png)
あとは、さらに横の"Creates spare parameters"ボタンをクリックすることで、そのVEXで設定しているchv、chfのパラメータを外に露出させます。あとは、必要に応じてパラメータの値を変更すると、その入力のジオメトリがねじれます。
![SnippetResult.png](https://qiita-image-store.s3.amazonaws.com/0/154191/b355c86b-0ac7-0985-1bfe-7cbff3881733.png)

###内積について

ポイントを軸で捻るWrangleを登録しましたが、このコードはHoudini扱える人、プログラムわかる人なら何をしているのかわかるかと思います。
Houdiniにすぐに挫折してしまう人の共通点は、数学が苦手って言います。
でも、このコードの内容は高校数学レベルにすぎません。そう、高校出ている人は学習しているはずです。
それでも、内積、外積って何？って状況でHoudiniを学習するのは厳しいです。
外積は、2つのベクトルの直交ベクトルを取得するのに使用するのは[Houdini Advent Calendar](http://qiita.com/advent-calendar/2016/houdini)14日目でわかりやすく説明されているので理解できると思います。

では、内積ってなんでしょうか？
わからないなら、どのチュートリアル動画見ても、:interrobang:となってしまいます。
そもそも、コンピュータグラフィックスを触っているのであれば、クォータニオンも外積も内積も親密に関わってくるはずです。
内積とは？

- 2つのベクトル間が0度以上～90度未満ならプラスの値
- 90度なら0
- 90度より大きいならマイナスの値

と答える人が多いのかなって思います。
じゃぁ、内積が返す数値は何？
例えば、内積が2を返したら、この2は何？
答えれる人は内積を理解していて、答えれない人は暗記型です。
内積は、めっちゃ使うことが多くて、上記の暗記型の知識だともったいないです。
そもそも、内積って難しい数式ではないです。

ベクトルAとベクトルBがあった時、内積は

```math
A・B = ||A||||B||cos(θ)
```
||A||はベクトルAの長さ
||B||はベクトルBの長さ
θはベクトルAとBの間の角度(角度の範囲は0度から180度)
です。
2つのベクトルの内積を・で表現するので、内積のことをドットプロダクトと言います。
プロダクトとは乗算のことです。

これは、
**ベクトルAの長さ**
と
**ベクトルBの長さ*cos(θ)**
の乗算結果だと思ってください。
![Dot.png](https://qiita-image-store.s3.amazonaws.com/0/154191/7b8389c7-4291-89c9-5029-5aabfebaecd7.png)
この図でベクトルBの長さ*cos(θ)は、ベクトルBをベクトルAに垂直に投影したベクトルCの長さになりますよね？
つまり、ベクトルAとベクトルBの内積とは、ベクトルAの長さとベクトルCの長さを乗算した値となります。
CGでよく使用する使い方は、
正規化したベクトルA(長さが1のベクトル)とそのままのベクトルBの内積を取得することが多いです。
これによって、ベクトルCの長さが返されますよね？
あとは、そのベクトルCの長さと正規化したベクトルAを乗算することでベクトルCが求まります。

だから何？
ベクトルB - ベクトルC
を計算してみてください。
![Dot2.png](https://qiita-image-store.s3.amazonaws.com/0/154191/3bf78cfc-babf-0abc-e0c1-4041ce4b8a4f.png)
このように、点から軸までの距離を表現したベクトル**も**取得することができます。

他にも、ベクトルAの長さをlength()を使用しないで求めることだってできます。
ベクトルAの長さ = dot(ベクトルA,normalize(ベクトルA))


今回使用したVEXコードの例では、このように内積を使用することで、
**ベクトルCで軸方向におけるポイントとの距離**を計算し、その距離の大きさに応じて角度を変化させることで捻りを表現しています。
例えば、Axisを(0,1,0)、Centerを(0,0,0)に設定すれば、Y軸方向におけるポイントと原点との距離が出ます。
Y座標がプラスの値ではプラスの方向に、マイナスの値ではマイナスの方向に回転します。

内積は、距離の計算だけでなく、屈折や反射の計算にも使用できるので知っておくべき知識です。

おしまい。:relaxed: