Houdiniで街のある景観をつくる！

![0_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/e6f0e1bc-f6c1-e868-2ecc-15a74d40720d.png)
# はじめに
この記事は　HoudiniApprenticeAdventCalendar2020　23日目の記事です。
Houdini Indie 18.5.351、SideFX Labs 18.5.397を使用しています。

hipファイルは [こちら](https://www.dropbox.com/s/l76hvo40i2i1giv/TownLandscape.hiplc?dl=0)

そこそこの規模のシーンファイルになっていますので、この記事では細かいノードの解説ではなく、今回試みた大まかな考え方に重点を置こうと思います。
Houdini は hip ファイルをたどって読み解けるのが最大の強みだと思います。
「解説が雑すぎてわかりずらい！」という場合は hipファイルを見ていただければと思います。
# もくじ
・[はじめに](#はじめに)

・[シーンファイルの全体の流れ](#シーンファイルの全体の流れ)
・[地形をつくる](#地形をつくる)
・[各種マスク素材をつくる](#各種マスク素材をつくる)
・[道をつくる](#道をつくる)
・[道に沿って家を並べる](#道に沿って家を並べる)
・[等高線をつくる](#等高線をつくる)
・[他の地域に家を配置](#他の地域に家を配置)
・[木を配置](#木を配置)
・[地形に色付け](#地形に色付け)

・[家のバリエーション生成](#家のバリエーション生成)
　　・[ベースのラインからベースの形を作る](#ベースのラインからベースの形を作る)
　　・[窓をつける](#窓をつける)
　　・[色をつける](#色をつける)

・[おわりに](#おわりに)

# シーンファイルの全体の流れ
![1_1_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/af2270d9-318d-5f4e-49a3-d9290f75afdc.png)
ネットワークは大きく分けて２つあります。
右側が「地形を作って家と木を配置」するメインのネットワークで、
左側が「家のバリエーション生成」するネットワークです。

大まかな流れは次の通りです。
__1.地形をつくる__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/02369b1a-19d0-f461-0e74-87b09826fb0b.png" width=50%>
__2.道をつくる__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/924fd15e-4a2e-d47a-a38c-0b37e4e4ca6b.png" width=50%>
__3.道の周りに家を配置__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a973c099-07ea-2e5b-3e28-0c119e1e20e2.png" width=50%>
__4.他の地域に家を配置__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/91f2c03c-afd0-cb17-ca28-250a36e74d31.png" width=50%>
__5.木を配置__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/6bd281a5-5b52-f367-f3c9-85ea987dc4de.png" width=50%>
__6.地形に色をつける__
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/e9a94f50-2fe8-e98f-3195-d5cdfeeec296.png" width=50%>

という感じです。
では、メインのネットワークを順に追いながら解説していきたいと思います。

# 地形をつくる
![2_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/8a08dbc9-16ea-3d39-96af-f10f8c364e91.png)
今回、地形の生成には Height Field を使用して簡易に作成しています。

最初のノード Height Field (長いので以後HF) では地形の大きさを指定していて、
次の HF Noise で大まかな山の形を作成しています。
この時、Offset 値に $F を入れて、フレームたびに違う形状を作るようにしています。
(つまりフレーム数をSeed値にしています。)
こうすることで、タイムスライダを動かすのを自動生成のトリガーにすることができます。
自動生成時に違う値を持たせたいパラメータには $F に何かしらの数字をかけた値を入れてばらつきを持たせています。

HF Mask by Feature と HF Blur で谷の形状をなめらかに、
HF distort と HF Noise でディテールをつけ、
HF clip で海となる部分を作成しています。
パフォーマンスの観点から今回は HF Erode を使わないようにしましたが、HF Erode を使用すると浸食のシミュレーションができてよりリアルな地形がつくれます。

# 各種マスク素材をつくる
![3_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a17b352a-5c8a-5808-f9ea-169a9ed49c06.png)
![3_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/bd65dbeb-1274-ef3a-c46c-9e07a014535b.png)

ここでは、HF Mask by Feature や HF Mask Noise などを使用して、今後使うためのマスク素材を作っておきます。
こういった作業に便利なのが「Labs HF Combine Masks」ノードで、マスク素材どうしを足したり引いたり掛け算したりすることができます。

road_source_Mask は次の項で道を作るために使用します。
HF Mask by Feature の 「Occlusion」 で凹んだ箇所を取得し、 「Slope」で傾斜のきつい場所を除外しています。
そういった箇所に道はできやすいんじゃないかというエリアを指定しています。

# 道をつくる
まず、Hight Field をポリゴン化し、Blast ノードで road_source_Mask の値がある場所だけ切り取ります。
![4_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/246a0372-68da-5bc8-5901-af1a8ca09341.png)

Transform の Scale Y を 0 にして高さ 0 の平面状態にして、
Labs Extract Silhouette 、Poly Path を使って輪郭線を抽出。
Smooth で滑らかにします。
![4_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/f71b1648-30c6-3d54-44bd-7e6c048cb14a.png)

Labs Straight Skeleton 2D を使用して中心線を作ります。
この形状が今回道に使う元になります。
![4_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/fd381f7e-f51a-eb82-091f-03955437e006.png)

ノイズを与えて道のうねり感を出し、VDB化→ポリゴン化で厚みを持たせたものを、
Labs Extract Silhouette で再度輪郭線を抽出して、縁取りのラインを作ります。
![4_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/c867957e-2029-9b2b-db0f-4789deffc694.png)

最後に道に家を並べる際の回転方向を指定するためにアトリビュート「N」と「up」を与えておきます。
![4_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/8e7b8975-2b5e-2a3f-3d39-5e0b589909d1.png)


# 道に沿って家を並べる
家はバリエーションが全種類マージされた状態のものと、それらのバウンディングボックスの状態のものを用意しておきます。
家は坂にコピーした際に浮かないように地下部分を長めにしてあります。
※家の生成自体は後の章で解説します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/6f8027e8-5ec5-186f-d839-6b243a71534d.png" width=50%><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/6234cc96-1e80-5bba-493c-67d0f86b6e46.png" width=50%>

まず、バウンディングボックスのほうを Chain SOP で並べます。
input1 に家のバウンディングボックス、input2 に先ほどの道のカーブを差します。
Chain SOP のパラメータは 「Rigidity」 オン、「Peace Spacing」 に値を入れて、隙間を与えます。
![5_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/234e33d2-e903-a776-98b5-348cceb297b5.png)

Chain SOP を使用する利点は、大きさの違いを考慮して並べてくれるため、重なりずらい特性があるからです。
下の画像はChain SOP で並べたものと、均等にResampleしたカーブにCopy to Pointsで並べた場合の比較です。
Resample & Copy to Points では大きいものが連続で来た場合に潜りあい、小さいものが連続でくると隙間が大きくなってしまいます。
（Chain SOP の解説は[16日の記事]((https://qiita.com/okada_takuya/private/0243bc0ef38f2761eef3))でも解説していますので、よかったらそちらもどうぞ）
![5_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a51b3e85-52b6-c0c7-a487-bed25c53faaa.png)

次のコンパイルブロック部分を解説します。
当初、Extract Centroid でバウンディングボックスの中心点をつくり、Ray SOP で元の Height Fieled にくっつけ、高さの位置合わせをしようとしていました。
しかし、それだけでは理想の高さにならなかったため、調整するための処理をここに内包しています。
（コンパイルしているのは、ループの高速化と、後にinvokeで使いまわししたいためです。）
![5_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/9cf634c5-650c-30aa-2b5f-5174d83f3bb4.png)

中心点の高さで家をコピーすると、下の画像のように坂にコピーした場合に一階部分が埋まってしまいます。
![5_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a30dda9d-42b9-3c68-ce71-d6023b3ee384.png)
中心点の高さではなく、四隅の点で一番高い高さにコピーする必要がありそうです。
そこでまず、 Labs Split Primitives by Normal で bounding box から床面だけを取り出し、それをRay SOP で HF にくっつけます。
![5_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/4072d646-0b72-330c-b6f4-b145a46a98ed.png)

For-Each Primitive ループ内で各平面を Match Size と Transform ノードを使用して、各平面の一番高いポイントの高さに他のポイントをそろえます。その後に Extract Centroid を使用することによって、xz軸は中心、y軸は一番高い位置にポイントを作成することができました。
![5_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/1b6106db-8623-748f-81c9-c6a6f7625960.png)

そのポイントに本番の家をコピーすると、窓が埋まらい高さにコピーすることができました。
![5_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/c277b7a8-9eaa-8c63-84f8-adacfe17b1a4.png)


# 等高線をつくる
道の周り以外の場所にも家をランダム配置していくのですが、全くの無秩序に配置するのではなく、
家の向きは基本的に地形の等高線に沿って向くように、また、道に近い家は道の向きに従うようにしたいと思います。

等高線はポリゴン化したHFを For-Each Numberにつなぎ、iteration で Clip する高さを変えて、
「Clip → 輪郭線を抽出 → 高さを0」
の作業をすることによって作っています。
また、resample 時に Tangent Attribute を「up」にすることで、up アトリビュートを持たせています。
![6_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/9ae87578-6461-349e-3042-898396270822.png)

次に、以前作った道のカーブをマージします。
![6_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/bc7f45c2-40c9-4108-303d-97835846ed1e.png)

Attribute Transfer で up をブレンドし、道に近いほど等高線の up は道の影響を大きく受けるようにします。
![6_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a8dd2a60-1949-8be0-debe-53bac36330b9.png)
家の向きを決める下地ができました。

# 他の地域に家を配置
家を配置するためのマスクを前段階で作ります。
まず、生成した道のジオメトリから HF Mask by Object を使用して道のマスクを得ます。
![7_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/12d4c303-1de2-bef9-3a9e-decce208d8a4.png)

HF Mask blur や　HF Mask Noise を使用して道の周りのマスクを作り、
以前作ったマスクと Labs HF Combine Masks で組み合わせて家を配置するためのマスクを作ります。
道の周りは濃くして家がたくさん配置されるように、高い場所は薄くして少なく配置されるようにしています。
![7_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/aea7d420-fa9d-2948-79bf-a1144aa6e2c3.png)

HF Scatter を使用してポイントをばら撒きます。
Scatter Method を「By Density using Mask Layer」にして Mask の濃いところに撒かれるように、
Keep Incoming Terrain をオフにすることで地形ジオメトリは含めないようにします。
![7_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/158aaa7a-ac4c-6173-e704-943e64e4fc84.png)

前の章で作成した等高線から up アトリビュートを Attribute Transfer でポイントにコピーします。
![7_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/8abb60be-1d40-f89c-aa55-8c64c3ee7f2e.png)

道の周りに家を配置した時と同様に、家の Bounding Box を一度配置し、一階が埋まらないように調整した高さで中心点を作り、その場所に家を配置します。
調整した高さの中心点を作る工程は Invoke Compiled Block で先ほどのコンパイルブロックを呼び出し、使いまわししています。
Invoke Compiled Block　はコンパイルしたネットワークを使いまわしすることができる便利なノードです。
↓ Invoke Compiled Block のヘルプはこちらです。
https://www.sidefx.com/ja/docs/houdini/nodes/sop/invoke.html
![7_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/3e30efeb-d8fd-4038-7d44-8f3a2992088f.png)

# 木を配置
今回配置する木は簡易なモデルを作成して使用しています。
![8_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/815964d2-1573-3e3e-5a69-7a67e4de1a1c.png)

家を配置した時と同様に、Scatter するためのマスクを作ります。
最初に作っておいた森のマスクから家や道の場所を抜いています。
![8_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/cedc3ec5-5eb6-e390-30ad-782c16ccb708.png)

家の時と同様に HF Scatter でばら撒いたポイントに木をコピーします。
![8_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/98318c8c-a471-c979-542e-6d4bff5401db.png)
# 地形に色付け
ここまで作ってきたマスクを利用して地形に色を付けます。
HFをポリゴンに変換後、Color SOP、Labs Color Gradient を使用して色を作成して、
Labs Color Blend を使用して色をブレンドしています。

![9_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/7bf66c31-9ddc-c139-a27b-66383795b959.png)

いままでのものをマージして完成です！
![9_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/dafebbfb-25fb-05af-c5db-ca22940763a0.png)


# 家のバリエーション生成
![10_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/ccf6c458-964d-21c1-1743-291e7bf573dd.png)

後回しにしていた家の自動生成について解説したいと思います。

###ベースのラインからベースの形を作る
最初はラインからはじめます。
長さや位置が多少ばらつきのあるラインを作ります。
ここでのラインの本数はシードによって数が変わり、建物の階数になります。
この場合は２階建ての家ということになります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/611d5670-6ed0-8bcb-a15f-22ddfad0c742.png" width=60%>

ラインごとのループに入り、一本ずつ（一階ずつ）処理していきます。
枝のようにラインを追加します。
これは seed によってあったりなかったり、2本あったりします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/7c205f96-af32-1625-ff07-f91788b8477c.png" width=60%>

それを Sweep で画像のような中心線のある平面を生成します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/46a2244d-7bb1-93bb-1a5a-8e5692f7edb4.png" width=60%>

Poly Extrude で押し出します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/25e65ecb-eb21-d224-0fd7-938dfb130685.png" width=60%>

中心線の上部だけを Transform で持ち上げます。
これで三角屋根の建物ができました。
お互いを Boolean で合体させておきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/c999781d-2ae1-1c30-8159-f21bb94c5ee0.png" width=60%>

2本目(2ループ目)のラインも同様の処理で2階建ての高さの建物を作ります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/c5a5f221-e721-14ea-c643-dbf27a3a42e3.png" width=60%>

それらを Boolean で合体させます。
最初が３本だった場合は3階建ての建物も更にbooleanで合体させます。
これでベースとなる家の形ができました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/b7742c20-29ab-fd6b-b2f3-3e579d0aa00f.png" width=60%>

ビューポートの画像でざっと説明してしまいましたが、今の処理をネットワークでみるとこんな感じです。
For-Loop with Feedback を使用した階ごとのループになっていて、前のループで作った建物にブーリアンで合成していく感じです。
![10_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/5471c112-5992-d623-4f8f-70c438348708.png)

細かいところは hip ファイルを見ていただければと思います。

### 窓をつける
後半のネットワークはディテールやパーツを付けるネットワークになっています。
![10_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/60f1fb10-a4ea-f242-82ad-fbe6bb029e3e.png)
似た処理が多いので、ここでは代表して窓の付け方だけ解説したいと思います。

まず、ベースの形から壁だけを取り出します。
事前にグループを設定しておき、Split ノードで取り出しています。
![11_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/5a59c7fa-ddff-c430-d1fb-cdf3835d42b5.png)

次に、Divide SOP の Bricker Polygons を使って階数ごとにポリゴン分割、
Labs Lot Subdivision である程度の大きさにポリゴンを分割します。
![11_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/eacc01d5-b935-e038-2f82-6fd5ac9381db.png)

Facet の Unique Points でポリゴンをバラバラにした後、(Point Split と同様の操作です)
Primitive Wrangle でイレギュラーを起こしやすい5角形以上のポリゴンを削除、
Measure で Area を測って Blast で小さい面積のポリゴンも削除しておきます。
![11_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/575bd24e-e811-4e33-f4e5-52f2075e2203.png)

ポリゴンごとの中心点をつくります。
Primitive SOP の Do Transform をオンにして、Scale を 0 にするとポリゴンごとの中心にポイントが集約されます。
その後　Fuse で一つのポイントにしています。
地下部分には窓は必要ないので Clip で削除しておきます。
Nとupのアトリビュートも付与しておきます。

![11_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/f26642d4-a491-35c0-397b-03f9cf1e65ea.png)

窓のジオメトリは簡易にモデリングしてあります。
![11_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/a3ae899d-2120-34e5-f33c-2ee6fc8385f2.png)

先ほどのポイントに Copy to Points でコピーしてきます。
![11_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/967415fd-7647-2b55-2494-1c65f55e9835.png)

もう一つ、box ノードを挟んで Bounding Box の状態のものもコピーしておきます。
こちらは壁に穴開けるために使用します。
![11_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/e78b8805-514e-12d0-a544-f9dd1289b416.png)

厚みを付けておいた壁を Boolean でくり抜きます。
![11_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/5d9025e1-075a-41dc-706d-37d18fb3e65a.png)

先ほどの窓と Merge で合体することで、窓の完成です。
![11_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/df7fa894-322d-d12e-b8cd-111973f45ecf.png)

他のパーツも似たような手順で作成、合成しています。
詳しくは hip ファイルをご覧ください。
![11_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/7bbb429c-3bfe-ad3c-49a7-81f823bff635.png)

### 色をつける
最後に色をつけます。
割り当てたい箇所ごとにグループを設定しておいて、グループごとに色指定用の乱数を作っておきます。
画像は屋根の色を指定しているところです。
Color SOP の Color Type を「Ramp from Attribute」にして、Attribute に作った乱数を指定します。
これでランプの色からランダムで選択されます。色の分布をコントロールするのにいい方法だと思います。
![12_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/6d7119b6-67d6-7d1c-ce17-2ebf7c57176a.png)

家のバリエーション生成が完成しました！
ネットワークは整理前で古いのですが、ターンテーブルで回したムービーをTwitterにあげています↓
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">お家の自動生成<br>我ながら、なかなか可愛いいものができた！<a href="https://twitter.com/hashtag/Houdini?src=hash&amp;ref_src=twsrc%5Etfw">#Houdini</a> は最高！ <a href="https://t.co/5bIrWKM4gb">pic.twitter.com/5bIrWKM4gb</a></p>&mdash; 岡田拓也 (@okada_takuya) <a href="https://twitter.com/okada_takuya/status/1331185850957262848?ref_src=twsrc%5Etfw">November 24, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Houdini は最高！


## ※追記（2024/11/20）
家のバリエーション生成ファイルは瓦を追加してHDA (hdalc) 化してみました。
![hdalc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535300/c404639a-3fcc-dc07-0fea-1b78582fdd74.png)

 [こちら](https://www.dropbox.com/scl/fi/4qla4e0q97ehouut1e3u6/sop_okad_housegenerator.2.0.hdalc?rlkey=0cblgmh48om5fkwpwwq7zv117&st=8um9jco0&dl=0)からダウンロードすることができます。


# おわりに
最後まで読んでいただきありがとうございます！
この作例では16日目の記事「[Houdini18.5 を使用した作例！](https://qiita.com/okada_takuya/items/0243bc0ef38f2761eef3)」の中で紹介している小技もいくつか使用しているので、併せてお読みいただくと hip が読み解きやすいかと思いますので、ぜひご覧ください！

また、今年はめんたいこさん主催のHoudiniゆるゆる会に何度か参加させていただきました。
ゆるゆる会のhipファイルも公開されていますので、勉強に役立てることができると思います。
ゆるゆる会connpassページ：https://houdini-teaparty.connpass.com/
hipファイルtrello：https://trello.com/b/7ftxZclJ/houdini%E3%82%86%E3%82%8B%E3%82%86%E3%82%8B%E4%BC%9A

来年も Houdini を学習していく一年になったらなと思っています。
それではよい Houdini Life を！