
2026/03/10 2:02
波動関数崩壊(Wave Function Collapse)を利用したプロシージャル・ヘックスマップの構築
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
元の要約を繰り返してください。
本文
4 100個の六角タイルで作るプロシージャル中世島々
WebGPU と大量のバックトラッキングを駆使して構築
- ライブデモを実行する
- GitHub にソースコードあり
各マップは異なる。すべて種付きで決定的。
子どもの頃からプロシージャルマップに夢中だった私
AD&D のダンジョンズ・マスターズガイドのランダムダンジョンテーブルでサイコロを振り回しながら、
「設計する」ではなく「発見する」――一室ずつ探検し、サイコロが宝箱かネズミの群れか決める。
数年後に自分だけのマップジェネレーターを作ることにした。
道路・川・海岸線・崖・森・村――すべてを完全にプロシージャルで生成する、
Three.js + WebGPU + TSL シェーダーで約4 100個の六角セル(19 個のグリッド)を20 秒程度で作り上げる。
カルカソンヌだがコンピュータがやる
核心技術は Wave Function Collapse (WFC)。
元々 Maxim Gumin が発案したアルゴリズムで、プロシージャル生成ゲーム開発者に大人気。
マップタイルを使った楽しさ
カルカソンヌをプレイしたことがあるなら WFC はすでに直感的に分かる。
タイルの端(草・道・都市)が隣接するタイルと一致しなければならない。
コンピュータはこれを高速で行い、詰まったらエラーを吐くだけ。
ひねり:六角タイルは四辺ではなく六辺
1つのタイルに 6 本の境界があるため、制約が 50 % 増える。
正方形 WFC は十分に研究済みだが、六角版はまだあまり踏まれていない。
タイル定義
- 30 種類(草・水・道路・川・海岸・斜面)
- 各タイルは 6 本の辺を持ち、さらに重みで優先度を決める。
- 回転数:6 通り
- 高低差レベル:5 段階
合計 900 個の状態がセルごとに存在。
{ "name": "ROAD_D", "mesh": "hex_road_D", "edges": { "NE": "road", "E": "grass", "SE": "road", "SW": "grass", "W": "road", "NW": "grass" }, "weight": 2 }
WFC の動作
- 無限に広がる状態
グリッド上のすべてセルを「全種類・全回転・全高」へ設定。 - 最も制約されたセルを崩壊させる
エントロピー(残り可能数)が最低のセルを選び、ランダムで状態決定。 - 伝播
選択したセルに合わせて隣接セルの不適合な状態を除去。 - 繰り返し
すべてのセルが確定するまで続ける。
詰まると、アルゴリズムは停止してしまうため、その部分が面白い。
マルチグリッド問題
WFC は小さなグリッドでは安定だが、
217 セルの六角グリッドであれば失敗はほぼ起きない。
対して 4 123 セルのグリッドになると頻繁に失敗する。
解決策:モジュラー WFC
- グリッドを19 個の六角形(2 ラジアン)に分割し、
- 各グリッドを独立して解くが、隣接セルで既に配置済みのタイルは固定制約として扱う。
境界に合わない場合、現在のグリッド内のバックトラッキングだけでは修正できず、
ここで多大な開発時間を費やした。
バックトラッキング
WFC は頻繁に失敗するため、
テキストブック通り「最後の決定を打ち消して別タイルを試す」ことが必須。
私のソルバーは伝播中に除去した状態(デルタ)のトレイルを保持し、
全体をコピーせずに高速で巻き戻し可能。
最大 500 回までバックトラッキングを試みるが、
それだけでは境界問題は解決できない。
復旧システム
- 第1層 – Unfixing
初期伝播時に矛盾が起きたら、固定制約から可変セルへ戻す。 - 第2層 – Local‑WFC
メイン解が失敗したら、問題領域を半径 2 の小範囲で再度 WFC を実行。
最大5 回まで試みる。これにより全マップの成功率は約86 %になる。 - 第3層 – Drop and Hide
最後の手段として、問題セルを完全に削除し山タイルで隙間を埋める。
山の崖エッジはあらゆるエッジと合致し、見た目も自然。
第三次元
マップは平面ではなく 5 層の高低差を持つ。
海・草はレベル0で始まり、斜面や崖で上下に移動。
レベル 3 の道路タイルは同じレベルか、段階を繋ぐ斜面タイルと接続しなければならない。
誤ると道路が崖へ終わったり、水が空へ流れたりする。
六角座標:思いのほか奇妙
六角は 6 本方向のため、2D x,y 座標への単純マッピングは存在しない。
オフセット座標(行列方式)は隣接検索や距離計算で不便。
解決策:キューブ座標 (q, r, s)
s = −q−r という関係を持つ 3 次元座標系。
隣接セルは 2 座標に ±1 を足すだけで簡単に求められる。
Amit Patel の Red Blob Games がこの方法を解説しているので、
六角グリッドを扱う際には必ず参照したほうがよい。
木・建物の配置と WFC ではない理由
初期段階で木や建物も WFC に任せてみたが、
WFC はローカル境界マッチングに強く、大規模パターン生成には向かない。
結果として木はランダムに散在し、建物は均等に広がるだけだった。
解決策:Perlin ノイズ
グローバルノイズフィールドで木の密度と建物配置を決定。
WFC とは独立して動作させることで、森林・集落などの有機的クラスタリングを実現した。
水:外見上もっと難しい
水面は単なる青い平面ではなく、アニメーション化されたカースティックスパークルと沿岸波が必要。
スパークル
「ゼルダ 風のタワー」のようなカートゥーン調のきらめきを目指し、
初期は Voronoi ノイズで生成した 4 層のカースティックを試したが GPU 負荷と見た目の両方に問題。
解決策:小さくスクロールするテクスチャをサンプリングし、単純ノイズマスクで合成。
沿岸波
Bad North の美しい海岸線エフェクトを模倣し、
距離ベースの正弦波を沿岸から外側へ放射させる。
各ピクセルが「陸地から何メートルか」を知るために、
全マップの白=陸地・黒=水のオーソグラフィック画像をレンダリングし、拡散とぼかしで勾配を作成。
シェーダはこの勾配を読み取り、距離ごとにアニメーション波を配置。
コーブ問題
直線的な海岸ではうまく機能するが、湾や入り江では波線が太く不自然になる。
試した手段:
- スクリーンスペース微分で勾配伸びを検知(あるズームレベルで動作し他では失敗)
- テクスチャ空間勾配大きさで対向岸の相殺を検出(狭い川のみ検知)
- 追加拡散パス(直線海岸も影響)
根本原因は、ぼかしが「最近の陸地の量」を表すだけで「最も近い海岸までの距離」ではない。
CPU 側で水セルごとに隣接を調べてコーブを検出し、別マスクテクスチャを書き込むことで波線を薄く抑えることに成功。
Blender でタイル作成
KayKit の低ポリ中世六角パックがベースだが、
サブ完備タイルセットには足りない接続部があったため、新規タイル(斜面川・河口・海岸への接続・複数の崖エッジ)を作成。
- 制約:すべてのタイルはワールド幅 2 単位で、六角境界に沿って正確に合致する必要がある。
- UV 配置ミスはほんの数ピクセルでも見える縫い目を生むため注意。
WebGPU と TSL シェーダー
レンダラーは Three.js + WebGPU で、カスタムビジュアルエフェクトは TSL(Three.js Shading Language)で実装。
TSL は GPU 上で動く JavaScript ライクな構文を持つ。
ポストプロセッシング
- GTAO Ambient Occlusion:タイル間の隙間を暗めにし、AO をノイズ除去してスプリンクルを減らす。半解像度で実行。
- Depth of Field:カメラ距離に応じたティルトシフトぼかしでミニチュア感。FOCAL 長さはズームに合わせて変化。
- Vignette + Film Grain:エッジの微妙な暗めとノイズでアナログ風。
ダイナミックシャドウマップ
フレームごとにカメラビューに合わせたシャドウマップ枠を計算。
見えている領域だけをライト座標系へ投影し、テクスチャタイルを最小化。ズームアウト時は低解像度で全マップを覆い、ズームイン時は個々のタイルに高精細シャドウ。
最適化
- BatchedMesh:各六角グリッドに対し 2 本(タイル・装飾)のバッチメッシュ。1 回の描画呼び出しで済む。
- 共有マテリアル:すべてのメッシュが同じマテリアルを共有し、シェーダーステートスイッチをゼロに。
結果として 4 100+ セル、38 本のバッチメッシュでデスクトップ・モバイルとも 60 FPS を維持。
要約
サイコロは不要。
ボタンを押すとマップが自動生成され、アルゴリズムが決めた配置を発見する感覚が味わえる。
道路や川のシステムが完全に一致し、毎回違う体験になる。
数値
| 指標 | 値 |
|---|---|
| タイル種別 | 30 |
| 六角グリッド数 | 19 |
| 合計セル数 | 約4 100 |
| グリッドあたり描画呼び出し | 2 |
| 最大バックトラッキング回数 | 500 |
| Local‑WFC 試行回数 | 5 |
| 全グリッド構築時間 | ~20 秒 |
| 500 回実行時成功率 | 100 % |
技術スタック
- Three.js r183 + WebGPU レンダラー
- TSL で全カスタムシェーダー
- Web Workers(WFC ソルビング)
- Vite ビルドツール
- BatchedMesh(効率的タイル描画)
- 種付き RNG(決定性・再現性)
試してみる
ライブデモで試せます。
hex ボタンをクリックするとマップが拡張され、「Build All」で全体生成。
50 以上のパラメータ調整可能な GUI パネルもあり、ライティング・カラーグレーディング・水エフェクト・WFC 設定など自由に変更できます。
ソースコードは GitHub に公開済みです。
作者について
Felix Turner
クリエイティブ開発者/Airtight Interactive の創設者。
インタラクティブビジュアル実験、WebGL/WebGPU エクスペリエンス、生成アートを手掛ける。
Twitter | Bluesky