見出し画像

楽しい建物ジェネレーターを模写してみた

9月16日にBlenderFesを視聴しました。その中で、平の字さんが「ジオメトリノードで世界をプロシージャル生成する」という公演を行いました。この中で、簡単な形状を詳細にして建物にする仕組みの基本的な部分を紹介されていたので、動画を見ながらHoudiniで同じようなものを作ってみました。


加工前のメッシュ
加工後のメッシュ

建物を指定のグリッドで分割する

上記の画像までの生成には大きく2つの工程があります。
一つは建物を指定したサイズのグリッドに分割し直す工程です。この工程はなくなっても次の処理を動かすことができます。

グリッド化をOFFにすると元の分割のまま建物の壁がアサインされる

まずは天面と底面を分離して壁だけの面を取り出します。GroupノードでNormalで取り出すこともできるのですが、最近はLabsのSplit Prim by Normalを使う事が多いです。このノードは最初からSplitの仕組みが入っているのがいいですね。

上下の面を分離

適切な分割数になるようにPrimitiveを分割します。DividSOPを使用して分割していますが、DividSOPの分割方法はResampleSOPの分割方法とは異なり、指定された分割の長さに近づけるために自動的に調整する設定がありません。効率的ではありませんが、ResampleSOPで生成されたポイント数を使用してDividSOPでの分割の長さを計算しました。

float len_x = primattrib(1,"restlength",0,0);
float len_y = primattrib(2,"restlength",0,0);

int p_x = npoints(1);
int p_y = npoints(2);

@nl_x = len_x / float(p_x-1);
@nl_y = len_y / float(p_y-1);

縦と横の長さを求めるために、エッジごとのY座標の長さを計算しました。Y座標の長さが0であれば縦のエッジだということがわかるので、その値を使ってSplitSOPで分割しています。

int p0 = primpoint(0,@primnum,0);
int p1 = primpoint(0,@primnum,1);

vector x1 = point(0,"P",p0);
vector x2 = point(0,"P",p1);

@len_y = x1.y - x2.y;
ResampleSOPの値をもとに壁を分割

分割した面を壁のメッシュに差し替える

分割した面ごとに準備した壁を配置します。CopyToPointSOPで配置しますが、Primitiveの中心にポイントを作るために、PrimitiveSOPとFuseSOPを使って中心に集約します。今回の方法では、オリジナルの壁の長さを変えないようにするため、コピーする壁の大きさをオリジナルの壁に合わせて変更しています。そのためグリッドで分割された面ごとの大きさを測り、scaleアトリビュートに代入しています。
長さを測るにはBoundSOPを使って求めたRadiiを使い、壁はX軸またはZ軸に平行なので、X軸の長さが0の場合はZ軸の長さを、それ以外の場合はX軸の長さを壁の横の長さとして使用しています。

vector scale = detail(1,"radii");
f@width = scale.x*2;
f@height = scale.y*2;
if(scale.x==0){
    @width = scale.z*2;
    }
壁のグリッドがそれぞれ用意したモデルなる

最後に最初に分離した上下のパーツをマージすれば完成です。
窓の大きさや有無が異なるパーツを用意してAttibute From Peicesで並べてみるのもいいでしょう。

壁のパターンを増やした

平の字さんも動画の中で話していましたが、このような仕組みの作り方を考えるときには、要素を分解して考えるのがとても重要です。
今回であればグリッドに分割する工程と、面ごとに用意したオブジェクトに差し替える工程は分離して考えることができます。
また、もっと凝った建物にするのであれば角にパーツを配置したり、壁が単純なランダムではなく、一定のパターンに基づくものを配置をするのも良いでしょう。
建物といえば、LabsのBuildingGeneratorがどうなっているのか気になってきました。今度解読してみようと思います。


この記事が気に入ったらサポートをしてみませんか?