Step Up Houdini！階段をつくろう

この記事はHoudiniアドベントカレンダー2017　１日目の記事です。

Houdiniには多くの機能があり、目的に対して様々なアプローチがあります。
（そこが面白いところでもあり、難しい所なのですが。）

今回は僕がHoudiniを最初に使いだした頃に取り組んでいた「階段」をテーマに
いろんな方法で階段を作っていきます。

＊hipファイル：https://drive.google.com/open?id=1cGAutLmJvjvVo4fKyvKneLq3ARVIUdiq


#「普通の階段」
利用SOP：Copy Stamp,Duplicate
![normal.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/13cdd536-d7c0-74fd-dfc2-d279d7a42c5c.png)

階段を１段ずつ複製します。
複製はCopy StampとDuplicateどちらでも可能です。
![copyanddupulicate.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/faf2bf8c-e8b2-15cd-0547-8aef81019a50.png)


この２つの違いはほとんどありません。duplicateのほうが
copyよりも単純なため少し早い程度の違いです。


#「らせん階段」
利用SOP：Resample,PolyFrame,Copy Stamp
![spiral.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/5d884da7-4073-2ed9-3b4e-afd9e8989968.png)
lineやcurveを利用して、階段を作成します
polyframeを利用すると、簡単に法線、接線、従接線を取得できます。
![polyff.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/0b8063da-ace5-146c-7704-b65f5c5ebf8f.png)

法線が正しく設定されていれば、copySOPが正しい向きに階段を配置してくれます。
![spiral2.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/598a531d-7069-42be-7a34-434618cd7e1b.png)




#「ぐにゃぐにゃ階段」
利用SOP：Divide,Foreach
階段が常に直線とは限りません。特殊な形状の階段を作るにはどうすればいいでしょうか。
上の段になるほど、横幅を縮めるstampやscaleを利用する方法もありますが、ここではdivideを利用してみます。

まず、階段にしたい形状を用意します。
![gunya1.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/ea04688f-c63d-dd7a-427f-d15df7221299.png)
Wrangleにはupvectorとpscaleのばらつきを設定します。

```c:points
@up={0,1,0};
@pscale=chf("stairwidth")+rand(@ptnum);
```
sweepを行います。
![gunya2.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/8ef0852f-4071-fe6d-0438-765716e7451a.png)


そのあとはdivideで一度内側のエッジを全て削除します。
![removesharededge.png](https://qiita-image-store.s3.amazonaws.com/0/151589/a6668f24-9b6e-9c9f-e474-82acde3b9ee5.png)
その直後にもう一度divide（BrickerPolygon）を行います。xとzは仮の数値として1000という大きい数値が入っています。
divide（BrickerPolygon）はバウンディングボックスの端を0として計算しているので、大きすぎる数値が入ると分割がされません。これを逆手にとってy方向のみ段差の高さで分割します。
![xz1000s.png](https://qiita-image-store.s3.amazonaws.com/0/151589/5d7c2c7c-4425-58e3-601f-12b39e7c4604.png)

段差別に分割されたので、段差ごとに平らにしていきます。段差1段につき1primitiveとなっているので、Foreach内でprimitiveのscaleをＹ座標：0にして平坦化させます。

![pettan0.png](https://qiita-image-store.s3.amazonaws.com/0/151589/b9ccaa11-7b11-c970-8b1b-b0670558f6a6.png)

#「岩の階段」
利用SOP：VoronoiFracture,VDB
![iwa.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/ba09842b-c0fb-9058-c084-4035e14a7cad.png)


ぐにゃぐにゃ階段の応用として、VDBを使って岩の階段を再現します。

岩は人工物ではないので、切れ目がランダムです。しかし、完全なランダムではありません。
なぜならば、人が歩きやすい階段にするために、あるルールをもって配置するからです。
では、この場合の配置ルールを想定してみます。

①１段の高さは等しい
②１段を２つに分割する必要はない
③切れ目は縦に切れている

以上のルールを実現するために、ボロノイを利用します。
ただボロノイをかけると、ボロノイっぽくなってしまうので、段差ごとにForeachをかけてseedの違うscatterを発生させます。
![voronoi6s.png](https://qiita-image-store.s3.amazonaws.com/0/151589/462977e2-34f9-a62a-deb8-9a4c925e0a33.png)


scatterの偏りを生じさせるために、scatter前後にZ方向の縮小をかけます。
右に行くほどscatterを行った際にZ方向の縮小が強くなっています。前段階のtransformでZ：0.5とした場合、元に戻すために後ろのtransformで1/0.5として、元の大きさに戻します。
![voronoi5.png](https://qiita-image-store.s3.amazonaws.com/0/151589/f5dafe2a-db81-0c43-1322-5f3f1116500f.png)


ボロノイで縦にしすぎると岩らしさが無くなってしまうので、ちょうどいい塩梅を見つけます。



ボロノイの注意点として、gridのように高さのない面の場合はそのまま切れるのですが
高さのある面の場合は、backfaceにサーフェスができることがあります。

ですので、一度上を向いているサーフェスだけ抽出します。
抽出するためには法線が上に向いているかを確かめればよいので、法線と{0,1,0}の内積をとります。
上を向いている場合は-1、下を向いている場合は1になっています。

```c:primitives
if(dot(@N,{0,1,0})<-0.5){
 removeprim(0,@primnum,1);
}
```
下向きのbackfaceは一度削除します。

次に岩の角を丸める処理を追加します。今回は、resampleを利用した後にrelaxをかけて丸みを追加します。
そして、polyextrudeを行った後、groupを利用して70度以上のedgeを取得します。そのgroupにpolybevelを
加えます。（なんとなく岩っぽく変形した？）駄目押しで、一度ポリゴンからVDBの変換を行います。
![voronoi7.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/999d28a7-c9c1-2d64-2a98-54fa36af02b2.png)

これらの一連の処理を岩1個ずつforeachで回します。本当は重くなるので、やりたくないのですが、
VDBの解像度をかなり上げないと岩同士がくっついてしまいます。
＊VDBSmoothで丸みをつけることや、VDBcombineのSDF～で岩の傷をランダムに与えることも可能です。

問題ないレベルになったら、converVDBでポリゴンに戻します。
最後はポリゴン数を抑えて、autoUVを適用します。


#「エスカレーター」
利用SOP：Wrangle（UVアニメーション）
![es0.gif](https://qiita-image-store.s3.amazonaws.com/0/151589/cb41eed1-b81d-47b3-de57-ebc3eba169d4.gif)


エスカレーターは足場をループをさせる必要があります。
今回は法線の向きに気を付けて、頂点をUVに沿って移動させる手法を取ります。

##UVをNURBS Curveから取得する
UVとNURBS Curveの相性は抜群です。なぜならば、NURBSの始点がUVの0、終点が1になるからです。UVTextureのTextureTypeはUniformSplineを選択します。
![UVNURBS.PNG](https://qiita-image-store.s3.amazonaws.com/0/151589/65297643-62eb-9518-f8a2-4d2dbbeb1981.png)

UVが取得できたので、一見このまま進めばいいように思えます。しかし、ここで落とし穴があります。
エスカレーターのようにUVがループ状になった場合、頂点とUVでずれが生じます。始点(0)と終点(11)は別のPositionにいるにも関わらず同じUVの値を持っています(0=1)。
例：UV0の頂点位置からUV1の頂点位置に移動すると一周したように思うが実際は頂点0の前の頂点11にいる
![UVNURBS3.png](https://qiita-image-store.s3.amazonaws.com/0/151589/4fd4d204-5220-f0e3-6605-c5df30d5734d.png)
この状態を解消するにはNURBS CurveのArc TypeをClosedからOpen Arcに変更するか、Carve(Curveじゃないですよ)を利用する必要があります。Carveの方がポリゴンをNURBSにコンバートした後の始点移動調整が簡単なので便利です。
![UVNURBS4.png](https://qiita-image-store.s3.amazonaws.com/0/151589/3826e2c5-fb09-e08e-1f1d-b38e8c70f488.png)

落とし穴はもう一つあります。
ループさせるために始点と終点をfuseで繋げようとするとどうなるでしょう。
![UVNURBS6.png](https://qiita-image-store.s3.amazonaws.com/0/151589/7df29383-5334-ee13-b85a-41a0cec35acf.png)

fuseするとattributeが補完されてしまうので(1+0)/2となり0.5となってしまいます。
解決策としては。Wrangleで

```c:points
if(@ptnum==0){
@uv.x=0
}
```

を直接代入するか、fuseを利用せず始点か終点のどちらかの頂点を削除した後addSOPで結合させます。

##addSOPでポイント番号順にVertexを繋げる
addSOPを利用してClosedな状態に変換します。addSOPはこのエスカレーター作成の肝となります。
なぜaddSOPか。それはpolyLineやpolyを簡単に作ることができるからです。
![Add3.png](https://qiita-image-store.s3.amazonaws.com/0/151589/5e614f9c-3982-ea56-4eed-0a15d856eb3f.png)

![UVNURBS7.png](https://qiita-image-store.s3.amazonaws.com/0/151589/581ac7d9-4d93-64a3-6d0e-90460a829a5f.png)
これを、ポイント番号が整列された４つのpolylineに適用すると、エスカレーターの形ができます。
![Add6.png](https://qiita-image-store.s3.amazonaws.com/0/151589/1c77dee1-27ca-eaa3-be1c-56348c7f5e47.png)


##primUVで頂点をぐるぐるする
全ての下準備が完了しました。最後はprimuvを利用して回転を行います。

```c:points
float uvp=@Time*ch("../control/speed");
float puv=(uvp+@uv.x)%1;
@P=primuv(1,"P",0,set(puv,0));
```
![esloop.gif](https://qiita-image-store.s3.amazonaws.com/0/151589/aa9d6b54-2107-8dc3-3861-3d1696674298.gif)

ポイントはpuv=(uvp+@uv.x)%1の「剰余(%)」です。UVは0から1の範囲で値があるので、単純にオフセットしていくと1を超えてしまいます。そこで余剰を行います。すると0~0.999はそのままに、1.0　が　0.0　、　1.2　が　0.2に変換されて取得できます。結果としてUV内でループが発生します。
そしてprimuvでポイントを＠Timeや@Frameなどを利用してオフセット分ずらします。

法線の方向は人が乗っている間は平衡にする必要があります。
そこでバウンディングボックス内に存在するpointのY座標の法線を0にしてnormalizeします。
![esloopN.gif](https://qiita-image-store.s3.amazonaws.com/0/151589/c8312f05-6997-9aa7-3588-e9782469a111.gif)


法線が正しく設定されたのでcopystampで台をコピーすればいい感じの方向に向いてくれそうですね。
最後に、手すりなどいろいろくっつけて、エスカレーターの完成です！


#まとめ

以上５つの階段でした。このように同じテーマだけど違うやつに取り組むと
自分の知らない機能についての知見も増えると思います。

様々な方法を知れば知るほどHoudini力はステップアップしますよ。

次は satoruhigaさんの「HoudiniをOSCタイムラインとして使う」です！