Wrangleでメンガーのスポンジ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151589/4221575d-0239-af11-3041-ab821491ae39.png)


以下は、Houdini上でWrangleを使ってメンガーのスポンジ（Menger Sponge）を生成する一例です。  
この例では、DetailWrangleノード内で再帰的に各立方体の中心位置とサイズ（pscale）を計算し、後段のCopy to PointsノードでCube（Box）をインスタンスする方法を示します。  


## 手順

1. **新規ジオメトリノードを作成**  
   HoudiniのNetwork Viewで新しいジオメトリ（Geometry）ノードを作成し、中に入ります。

2. **Detail Wrangleノードの配置**  
   中に「Attribute Wrangle」ノードを作成し、**Run Over** を「Detail (only once)」に設定します。  
   このWrangleで、再帰的なメンガーのスポンジの点群（各立方体の中心位置とサイズ）を生成します。

3. **パラメータの追加**  
   Wrangleノードのパラメータペインで、以下のパラメータを作成してください。  
   - `iterations` (Integer)：再帰回数（例：3～4程度）  
   - `size` (Float)：初期の立方体のサイド長（例：1.0）

4. **以下のVEXコードをDetail Wrangleに貼り付け**

   ```c
   // パラメータ取得
   int iterations = chi("iterations");   // 再帰回数
   float init_size = chf("size");          // 初期立方体のサイド長
   vector start_pos = {0, 0, 0};           // 初期位置（原点）

   // 配列を用意：positions と scales
   vector positions[];   // 各立方体の中心位置
   float scales[];       // 各立方体のサイド長

   // 初期状態：1個の立方体（原点、サイズ = init_size）
   append(positions, start_pos);
   append(scales, init_size);

   // 各反復で、各立方体を3x3x3に分割し、メンガーのスポンジとして不要な部分を除去する
   for (int iter = 0; iter < iterations; iter++)
   {
       vector new_positions[];  // 次段での立方体中心位置
       float new_scales[];      // 次段での立方体サイズ
       int n = len(positions);
       for (int j = 0; j < n; j++)
       {
           vector pos = positions[j];
           // 子立方体のサイズは親のサイズの1/3
           float s = scales[j] / 3.0;
           // 3x3x3のグリッド（ix, iy, iz = 0,1,2）
           for (int ix = 0; ix < 3; ix++)
           {
               for (int iy = 0; iy < 3; iy++)
               {
                   for (int iz = 0; iz < 3; iz++)
                   {
                       // 3軸それぞれで、中央（値が1）の数をカウント
                       int cnt = 0;
                       if (ix == 1) cnt++;
                       if (iy == 1) cnt++;
                       if (iz == 1) cnt++;
                       // メンガーのスポンジのルール：中央および面中央は除去（カウントが2以上なら除外）
                       if (cnt < 2)
                       {
                           // 子立方体の中心位置は、親の中心から (ix-1, iy-1, iz-1) * s だけずらす
                           vector newpos = pos + set((ix - 1) * s, (iy - 1) * s, (iz - 1) * s);
                           append(new_positions, newpos);
                           append(new_scales, s);
                       }
                   }
               }
           }
       }
       // 次の反復用に更新
       positions = new_positions;
       scales = new_scales;
   }

   // 最終的に生成された各立方体の中心にポイントを作成し、"pscale" 属性にサイズを設定
   int npts = len(positions);
   for (int i = 0; i < npts; i++)
   {
       int pt = addpoint(0, positions[i]);
       // Copy to Pointsノードでインスタンスする際、pscale属性でサイズが決まる
       setpointattrib(0, "pscale", pt, scales[i], "set");
   }
   ```

5. **Copy to Pointsノードの配置**  
   - Detail Wrangleノードの出力に接続して、「Copy to Points」ノードを配置します。  
   - 2番目の入力（右側）に、インスタンスとして用いるBox（Cube）ノードを接続してください。  
     ※Boxのサイズは1単位としておき、pscale属性により各立方体のサイズが適用されます。

6. **表示と調整**  
   - 作成したCopy to Pointsノードを表示すると、指定した反復回数に応じたメンガーのスポンジが得られます。  
   - Wrangle内の`iterations`や`size`の値を変えて、生成のレベルや大きさを調整してください。

以上の手順とコードで、SOPとWrangleを組み合わせたメンガーのスポンジの生成が可能です。  
この基本手法をもとに、さらに色付けや変形、アニメーションなどを加えることで、より複雑な表現にも発展させることができます。


## グラデーション例

```c
// バウンディングボックスの最小／最大値を取得
vector bbmin, bbmax;
getbbox(0, bbmin, bbmax);

// ジオメトリ中心を算出
vector center = (bbmin + bbmax) * 0.5;

// バウンディングボックスの半径（中心から最も遠い距離のおおよその値）
float max_radius = length(bbmax - bbmin) * 0.5;

// 各ポイントの中心からの距離を計算
float d = length(@P - center);

// 0～max_radius を0～1に正規化
float t = fit(d, 0, max_radius, 0, 1);

// Rampパラメータ "color_ramp" に基づいてグラデーションカラーを設定
@Cd = relbbox(0, @P)*chramp("color_ramp", t);

```

メンガーのスポンジについては
すでに数年前に堀川さんがYoutubeにもあげられています
https://www.youtube.com/watch?v=-bass6YzWEA