22
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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

0_0.png

はじめに

この記事は HoudiniApprenticeAdventCalendar2020 23日目の記事です。
Houdini Indie 18.5.351、SideFX Labs 18.5.397を使用しています。
hipファイルはこちら

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

もくじ

はじめに

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

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

おわりに

シーンファイルの全体の流れ

1_1_3.png
ネットワークは大きく分けて2つあります。
右側が「地形を作って家と木を配置」するメインのネットワークで、
左側が「家のバリエーション生成」するネットワークです。

大まかな流れは次の通りです。
1.地形をつくる

2.道をつくる

3.道の周りに家を配置

4.他の地域に家を配置

5.木を配置

6.地形に色をつける

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

地形をつくる

2_1.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
3_8.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

Transform の Scale Y を 0 にして高さ 0 の平面状態にして、
Labs Extract Silhouette 、Poly Path を使って輪郭線を抽出。
Smooth で滑らかにします。
4_2.png

Labs Straight Skeleton 2D を使用して中心線を作ります。
この形状が今回道に使う元になります。
4_3.png

ノイズを与えて道のうねり感を出し、VDB化→ポリゴン化で厚みを持たせたものを、
Labs Extract Silhouette で再度輪郭線を抽出して、縁取りのラインを作ります。
4_4.png

最後に道に家を並べる際の回転方向を指定するためにアトリビュート「N」と「up」を与えておきます。
4_5.png

道に沿って家を並べる

家はバリエーションが全種類マージされた状態のものと、それらのバウンディングボックスの状態のものを用意しておきます。
家は坂にコピーした際に浮かないように地下部分を長めにしてあります。
※家の生成自体は後の章で解説します。

まず、バウンディングボックスのほうを Chain SOP で並べます。
input1 に家のバウンディングボックス、input2 に先ほどの道のカーブを差します。
Chain SOP のパラメータは 「Rigidity」 オン、「Peace Spacing」 に値を入れて、隙間を与えます。
5_3.png

Chain SOP を使用する利点は、大きさの違いを考慮して並べてくれるため、重なりずらい特性があるからです。
下の画像はChain SOP で並べたものと、均等にResampleしたカーブにCopy to Pointsで並べた場合の比較です。
Resample & Copy to Points では大きいものが連続で来た場合に潜りあい、小さいものが連続でくると隙間が大きくなってしまいます。
(Chain SOP の解説は16日の記事でも解説していますので、よかったらそちらもどうぞ)
5_4.png

次のコンパイルブロック部分を解説します。
当初、Extract Centroid でバウンディングボックスの中心点をつくり、Ray SOP で元の Height Fieled にくっつけ、高さの位置合わせをしようとしていました。
しかし、それだけでは理想の高さにならなかったため、調整するための処理をここに内包しています。
(コンパイルしているのは、ループの高速化と、後にinvokeで使いまわししたいためです。)
5_5.png

中心点の高さで家をコピーすると、下の画像のように坂にコピーした場合に一階部分が埋まってしまいます。
5_6.png
中心点の高さではなく、四隅の点で一番高い高さにコピーする必要がありそうです。
そこでまず、 Labs Split Primitives by Normal で bounding box から床面だけを取り出し、それをRay SOP で HF にくっつけます。
5_8.png

For-Each Primitive ループ内で各平面を Match Size と Transform ノードを使用して、各平面の一番高いポイントの高さに他のポイントをそろえます。その後に Extract Centroid を使用することによって、xz軸は中心、y軸は一番高い位置にポイントを作成することができました。
5_9.png

そのポイントに本番の家をコピーすると、窓が埋まらい高さにコピーすることができました。
5_7.png

等高線をつくる

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

等高線はポリゴン化したHFを For-Each Numberにつなぎ、iteration で Clip する高さを変えて、
「Clip → 輪郭線を抽出 → 高さを0」
の作業をすることによって作っています。
また、resample 時に Tangent Attribute を「up」にすることで、up アトリビュートを持たせています。
6_1.png

次に、以前作った道のカーブをマージします。
6_2.png

Attribute Transfer で up をブレンドし、道に近いほど等高線の up は道の影響を大きく受けるようにします。
6_3.png
家の向きを決める下地ができました。

他の地域に家を配置

家を配置するためのマスクを前段階で作ります。
まず、生成した道のジオメトリから HF Mask by Object を使用して道のマスクを得ます。
7_1.png

HF Mask blur や HF Mask Noise を使用して道の周りのマスクを作り、
以前作ったマスクと Labs HF Combine Masks で組み合わせて家を配置するためのマスクを作ります。
道の周りは濃くして家がたくさん配置されるように、高い場所は薄くして少なく配置されるようにしています。
7_2.png

HF Scatter を使用してポイントをばら撒きます。
Scatter Method を「By Density using Mask Layer」にして Mask の濃いところに撒かれるように、
Keep Incoming Terrain をオフにすることで地形ジオメトリは含めないようにします。
7_3.png

前の章で作成した等高線から up アトリビュートを Attribute Transfer でポイントにコピーします。
7_4.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

木を配置

今回配置する木は簡易なモデルを作成して使用しています。
8_1.png

家を配置した時と同様に、Scatter するためのマスクを作ります。
最初に作っておいた森のマスクから家や道の場所を抜いています。
8_2.png

家の時と同様に HF Scatter でばら撒いたポイントに木をコピーします。
8_3.png

地形に色付け

ここまで作ってきたマスクを利用して地形に色を付けます。
HFをポリゴンに変換後、Color SOP、Labs Color Gradient を使用して色を作成して、
Labs Color Blend を使用して色をブレンドしています。

9_1.png

いままでのものをマージして完成です!
9_2.png

家のバリエーション生成

10_0.png

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

ベースのラインからベースの形を作る

最初はラインからはじめます。
長さや位置が多少ばらつきのあるラインを作ります。
ここでのラインの本数はシードによって数が変わり、建物の階数になります。
この場合は2階建ての家ということになります。

ラインごとのループに入り、一本ずつ(一階ずつ)処理していきます。
枝のようにラインを追加します。
これは seed によってあったりなかったり、2本あったりします。

それを Sweep で画像のような中心線のある平面を生成します。

Poly Extrude で押し出します。

中心線の上部だけを Transform で持ち上げます。
これで三角屋根の建物ができました。
お互いを Boolean で合体させておきます。

2本目(2ループ目)のラインも同様の処理で2階建ての高さの建物を作ります。

それらを Boolean で合体させます。
最初が3本だった場合は3階建ての建物も更にbooleanで合体させます。
これでベースとなる家の形ができました。

ビューポートの画像でざっと説明してしまいましたが、今の処理をネットワークでみるとこんな感じです。
For-Loop with Feedback を使用した階ごとのループになっていて、前のループで作った建物にブーリアンで合成していく感じです。
10_8.png

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

窓をつける

後半のネットワークはディテールやパーツを付けるネットワークになっています。
10_9.png
似た処理が多いので、ここでは代表して窓の付け方だけ解説したいと思います。

まず、ベースの形から壁だけを取り出します。
事前にグループを設定しておき、Split ノードで取り出しています。
11_1.png

次に、Divide SOP の Bricker Polygons を使って階数ごとにポリゴン分割、
Labs Lot Subdivision である程度の大きさにポリゴンを分割します。
11_2.png

Facet の Unique Points でポリゴンをバラバラにした後、(Point Split と同様の操作です)
Primitive Wrangle でイレギュラーを起こしやすい5角形以上のポリゴンを削除、
Measure で Area を測って Blast で小さい面積のポリゴンも削除しておきます。
11_3.png

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

11_4.png

窓のジオメトリは簡易にモデリングしてあります。
11_5.png

先ほどのポイントに Copy to Points でコピーしてきます。
11_6.png

もう一つ、box ノードを挟んで Bounding Box の状態のものもコピーしておきます。
こちらは壁に穴開けるために使用します。
11_7.png

厚みを付けておいた壁を Boolean でくり抜きます。
11_8.png

先ほどの窓と Merge で合体することで、窓の完成です。
11_9.png

他のパーツも似たような手順で作成、合成しています。
詳しくは hip ファイルをご覧ください。
11_10.png

色をつける

最後に色をつけます。
割り当てたい箇所ごとにグループを設定しておいて、グループごとに色指定用の乱数を作っておきます。
画像は屋根の色を指定しているところです。
Color SOP の Color Type を「Ramp from Attribute」にして、Attribute に作った乱数を指定します。
これでランプの色からランダムで選択されます。色の分布をコントロールするのにいい方法だと思います。
12_3.png

家のバリエーション生成が完成しました!
ネットワークは整理前で古いのですが、ターンテーブルで回したムービーをTwitterにあげています↓


Houdini は最高!

おわりに

最後まで読んでいただきありがとうございます!
この作例では16日目の記事「Houdini18.5 を使用した作例!」の中で紹介している小技もいくつか使用しているので、併せてお読みいただくと hip が読み解きやすいかと思いますので、ぜひご覧ください!

16日目の記事でも書いたので、繰り返しになりますが…
Slackの「Houdini部」にもいくつか hip ファイルをアップしているので、興味ある方は参加してみてください。
質問コーナーではどんな初歩的な質問でも、私ふくめて部員の皆さんが誠実に答えてくれると思います。(たぶん)
https://houdinibu.slack.com/join/shared_invite/zt-dumulued-xW9yRhU23Oa34hVAk9Vc1w#/

また、今年はめんたいこさん主催の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 を!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
22
Help us understand the problem. What are the problem?