【Blender/GeometryNodes】RepeatZoneノードを使って高さの異なるオブジェクトを積み上げる

![untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/1a8c638a-6356-fbf4-3da0-3724e9cf9e66.png)
今回はBlenderのGeometry Nodesに4.0から追加されたRepeatZoneノードを使って  
異なる高さのオブジェクトを空高く積み上げる方法を紹介します。

### RepeatZoneの挙動
RepeatZoneノードは特定の処理を反復するノードです。

https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/utilities/repeat_zone.html

簡単なサンプルを作ってみます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/4b7e797b-0bfc-e5a4-3a50-b6f21546d45b.png)

新しく作ったCubeにGeometry Nodesを適用したらRepeat Zoneノードを追加します。  
このノードはベージュの枠内の中の処理をIterationsの回数分反復します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/63aa1837-588e-758a-d46e-d507e4f86ad2.png)

RepeatZoneの中にJoin Geometry→Set Positionと繋ぎます。  
Set PositionのOffsetはZ:0.5で、Z方向に位置が変わるように設定します。  
Group InputとJoin Geometryで合流させます。  
そうするとリピートする度にCubeがZ方向へ移動しながら増えていくことが確認できます。

### GeometryNodes完成版
それでは初めていきましょう。
全容をざっくり掴むため、まずは完成版のノードをお届けします。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/ccf854b5-6327-1e85-6dde-e687bc2144d8.png)

ネストしたGroupNodesの中身  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/1a207012-27cc-185c-fb0a-44ae5f76c0de.png)

### システムの仕組み
仕組みとしては、指定のcollectionからランダムに取り出したオブジェクトを  
Instance on Pointsで1点に配置、  
そのオブジェクトのバウンディングボックスの高さを取得し  
Z Up方向にオフセットして配置…を指定回数分繰り返すというものです。

### 手順前編:オブジェクトをランダムに取り出す仕組み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/a82a4a30-a0d7-90cf-741c-3cb5acfcb63e.png)

とりあえずサンプルオブジェクトを任意の数配置して、新規Collectionに格納しておきます。  
私はCone、Icosphere、Suzzanneの3体をTestObjというCollectionに格納しました。

Collection Infoノードを追加し、Join Geometry ノードに接続します。  
Collection Infoから先ほど作ったCollectionを指定すると、  
右上のビューに3体のオブジェクトが同時に表示されました。  
全部表示されているのでゴチャっとしていますので、こちらを  
1つずつ取り出すノードをここから組んでいきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/e6d231d0-b6ee-f3dc-6577-bf319ce1b35f.png)

ノードを追加し、少し組み換えます。  
まず、Instance on Pointsノードを追加します。つないだGeometryのPoint上に  
Instanceを表示するノードです。  
そして、Pointsノードを追加し、Instance on PointsのInputに繋ぎます。  
先ほど追加したCollection InfoをInstance on PointsのInstanceのInputに繋ぎ  
Instance on Points→Pick Instance、Collection Info→Separate Children/Reset Childrenにもチェック。  
これでInstanceとして1つずつオブジェクトが取り出せるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/91c9a2b4-fc95-7d1a-11b2-0f35f353998e.png)

ランダムのObjectをInstanceとして取り出すノードを組みます。  
Instance on PointsのInstance Indexに  
Random Valueノードを追加し、Integerに変更し整数が取り出せるようにします。

Addノード、Domain Sizeノードを追加し、Collection Infoから  
図のように順番に繋いでいきます。  
Domain SizeはCollection Infoに格納されているオブジェクトの数で、  
1を追加している理由としては、Random ValueではMinからMax-1の間の整数が取り出されるためです。
現状だとオブジェクトの数は3つなので「0,1,2」が取り出されるのですが
Instance Indexとしては「1,2,3」としないと3つ目のオブジェクトが
出現しなくなるため1を追加します。

ランダムに取り出す仕組みができたのでGroupNodesでまとめていきます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/25437d14-3c1c-9b32-d3fa-13c3fb10ccb4.png)

上記を選択してCtrl+Gでまとめます。

GroupInputの中に入ります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/6c8400d2-fcd5-0441-1cb6-bfd0cd5e9833.png)

親階層からIDとSeed値を取り出したいので、Random ValueのIDとSeedを  
Group Inputノードに接続します。  
右のGroupタブも連動して追加されます。

### 手順後半:オブジェクトを繰り返しオフセットして配置する仕組み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/d5aa8c2b-cef3-faf1-6525-a02c566ec746.png)

ここから、Tabキーで親階層に上がり、オフセットする仕組みを作っていきます。

繰り返す度に別のオブジェクトが出現するようにするため、  
先ほどのRandom IDを毎回違うものにします。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/8fd604c8-393b-e01e-7672-2ae5b1187513.png)

右のRepeatノードのItemsにIDを追加し、Integerとします。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/6d82a46d-3aa3-6351-f1ef-35dc936f6ebd.png)

IDが繰り返す度に1加算されるように  
RepeatZoneの始まりと終わりの間にAddノードで1.0を加算して、  
RepeatZoneの出口と繋ぎます。  
Repeatの入口を先ほど作ったGroupNodesのIDに繋いで、RandomのIDが  
繰り返す度に加算されたものがRandomのIDに渡るようになり、  
毎回違うオブジェクトが出やすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/c9ab951c-38c6-b3d2-4332-c2a02f3ce9be.png)

RepeatZoneの始まりとJoin Geometryの間にSet Positionを追加して、Zに任意の値を入れ、  
Repeat ノードの Iterations の値を上げてみると、ランダムなオブジェクトが積み上がって  
いくのが確認できました。

しかしこのままではオブジェクト同士が重なってしまい問題があります。  
それぞれのオブジェクトのBoundingBoxの高さ分Z方向にオフセットする仕組みを作っていきます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/be56ee74-5305-d598-3ea4-4481cb89872b.png)

GroupNodesからJoin Geometryに繋がっていたノードをCtrl＋右クリックで切り、  
Realize Instancesノード、Bounding Boxノードを追加して  
GroupNodesからJoin Geometryまでを順番に繋いでいきます。  
そうするとオブジェクトが四角く変化します。これはそれぞれのオブジェクトのBounding Boxです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/211493c3-69ed-914d-ac18-45cfb1368c6a.png)

追加するノードが増えるので注意しましょう。(赤い囲み)  
オブジェクト全体のBounding Boxと、ランダムのオブジェクトのBounding Boxの高さを  
Addノードで加算します。こちらをSet PositionのOffsetに繋ぎます。  
前に追加していたSet Positionは使わないので接続を切ってしまって大丈夫です。

ノードがぐちゃっとして見づらいときは  
Shift＋右クリックでノードの分岐点を追加し、整理します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/b1440e97-dd89-3742-a4b5-6c6b1ee03527.png)

Offsetされたランダムのオブジェクトが表示されました。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/f65e25bf-54ef-600c-ac76-6d589acdd54e.png)

VectorのXYZ全てに1が加算されてループされてしまっているため  
右奥斜めにどんどんオフセットされてしまっていますが、今回使いたいのはZ方向だけのため、  
Vector MathのMultiplyノードを追加、AddとSet Positionの間に挿入し、  
XとYに0をかけて動かないようにします。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3901849/67acb7ed-1e0f-b819-b2d1-0c6378660268.png)

完成です！  

いかがでしたでしょうか？  
配置場所を工夫することで色々な表現に使えそうですね。  
色々とセオリーはあるようなのですが、下記の動画の方法が  
とても参考になりました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/vLo73aw8Xj0?si=73TR87wVjy-ytmsk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

次回はこちらにランダムの回転を加える方法を追加解説しようと思います。