平らな島を捨てて、惑星にした
空に浮かぶ平らな島だったトップページを、宇宙に浮かぶ球体に作り替えた。参考写真を測って数字を2つ否定され、雑居ビルを290棟で止めるまでの記録。
#Three.js#ボクセル#設計
7月の終わり、ポートフォリオの土台を作り替えることにしました。それまでのトップページは、空に浮かぶ平らな島の上に建物が立っていて、カメラがその周りを一定の高さでぐるぐる回る、という作りでした。これを、宇宙に浮かぶ球体の上に、建物が360度立っている形に変えたかったのです。
言葉にすると「地面を平らから丸にするだけ」です。でも実際には、平らな地面の上では当たり前だったことが、1つずつ壊れていきました。この回は、その記録です。
「惑星」という言葉に引っ張られかけた
このサイトは、AI の Claude Code と対話しながら作っています。最初につまずいたのは、実装ではなく言葉でした。「惑星にしたい」と伝えたとき、Claude には、大気があって海があって雲が流れている、天体そのものを作る話として伝わりかけていました。
僕が考えていたのは、少し違うものです。球体はあくまで土台で、宇宙にしたいのは雰囲気。周りを飛ぶカメラは人工衛星。 そう言い直したことで、決まったことが3つあります。
- 遠くの景色をぼかしていた霧(フォグ)は「大気」の表現なので外す。代わりに、遠くのものは暗さで沈める
- 空から降っていた紙吹雪は、宇宙では落ちない。漂う星屑に作り替える
- 「カメラが一定の高さを回る」という前からあった仕様に、初めて物理的な裏づけがついた。衛星の軌道の高さだからです
言葉の定義が1つそろっただけで、設計がいくつも決まる。この回でいちばん効いたのは、コードではなくこのやり取りでした。
「自由に飛べる」をモードにしなかった
球になると、横に回るだけでなく、上下にも回り込んで惑星を眺めたくなります。設計の途中で、そのための「自由視点モード」を作る案が出てきました。自由に飛んでいるあいだは、画面の左に出ている説明カードや、建物の上に浮いている目印を引っ込める、という設計です。
僕の答えは「ただ回転できるだけでいい。カードも目印も普段どおり出す」でした。モードを作らなければ状態が増えません。 状態が増えなければ、「カードとカメラが互いに相手を書き換えない」という、それまで守ってきた決まりもそのまま保てます。
「放っておいたら元の軌道に戻る」も、独立した機能にはしませんでした。自由に飛んでツアーの軌道から外れても、手を離して何もしなければ、いつもの自動の周回が再開して、カメラは軌道へ引き戻されながら回り続ける。アイドル中の周回と、軌道への復帰が、同じ1つの仕組みになるので、タイマーで状態を切り替える必要がなくなりました。
参考写真を測ったら、決めたばかりの数字が2つ否定された
途中で、目指している見た目の参考画像が届きました。小さな球体に街がびっしり立っていて、建物が全方向へ放射状に突き出している、ミニチュア写真のような惑星です。
この写真を、目で見るだけでなく測ることにしました。惑星の中心から720方向に線を引いて、外側から内側へたどり、背景から物体に切り替わる境目を探す。背景が中央ほど明るいグラデーションだったせいで、切り出しは2回失敗し、3つ目のやり方でようやく測れました。
結果はこうでした。
| 項目 | 実測 |
|---|---|
| 建物の高さ(中央値) | 地表の半径の +17% |
| 建物の高さ(最大) | 地表の半径の +30% |
| 地表がむき出しの方向 | 26% |
これで、決めたばかりの数字が2つ否定されました。
1つ目は、建物が高すぎたことです。惑星の半径を22にすれば、一番高い塔が半径の46%になって「惑星に建つ塔」に見えるはずだ、と考えていました。でも参考写真では、最大でも30%です。46%では、参考写真のどのビルよりも高い。惑星の半径を34に改めて、塔がちょうど30%になるようにしました。
2つ目は、ランドマークの建物5つだけでは、この見た目にならないことです。参考写真では74%の方向に何かが立っています。5つでは、惑星がほとんどつるつるに見えてしまう。そこで、名前を持たない雑居ビルの群れを足すことにしました。
目で見て「いい感じ」に合わせていたら、どちらも気づかなかったと思います。写真から数字を取り出して、自分の数字と突き合わせる。これは今でも、このサイトを作るうえでの決まりになっています。
290棟で止めた理由
雑居ビルは、参考写真と同じ「むき出しの方向26%」を目標に並べるつもりでした。同じ720方向の線を、今度は3D の空間の中で引いて測る仕組みも作りました。
ところが、実際に測ってみると、26%に届かせるには建物が1,000〜1,400棟、立方体の数で10万〜16万個も必要でした。小さなビルで大きな球の4分の3を覆うには、それだけ敷き詰めるしかない。計算の間違いではなく、単純に幾何がそれだけの量を要求していたのです。
そして、その量は重すぎました。雑居ビルなしで約5秒で撮れていた確認用のスクリーンショットが、1,351棟では20〜30秒かかるようになりました。棟数を落としながら測り直して、最後は290棟にしています。むき出しの方向は、目標の26%ではなく約75%。狙いとはほぼ反対の位置に着地しました。
ここで大事にしたのは、密度を決め打ちで埋め込まないことです。街を並べる関数自体は密度に依存しない作りのままにして、「密度は描画の重さで頭打ちにした」という判断を、コードのコメントとテストの両方に残しました。あとで実機で余裕があると分かれば、数字を1つ変えるだけで密度を上げ直せます。
地面を、ボクセルから普通の球へ
最初の土台は、建物と同じように小さな立方体を積み上げた球の殻でした。その作り方を決めるときも、先に測っています。殻の厚さを立方体1個ぶんにすると、中心から2万本の線を撒いたうち3〜4%が、どの立方体にも当たらずに抜けました。斜めから見ると、ブロックの角どうしの隙間から向こう側が見えてしまうのです。厚さを2個ぶんにすると、抜ける線は0になりました。
ただ、その殻を実際に画面で見たあと、「直径を半分にして、ボクセルではなく普通の滑らかな球にしたい」と頼みました。見てから決めたかったので、まず試しに作ってもらい、スクリーンショットを見て、この方向で進めることに決めました。滑らかな球の上に、ボクセルの建物が刺さっている構図です。
このとき、ボクセルの地形を作るためのコードとテストは消しませんでした。今は使っていないだけで、表示する部品を1つ差し替えれば戻せます。実際このあと、9月にもう一度ボクセルの地面に戻す案を試しています(結局、粗すぎるという理由でまた見送りました)。試して捨てた案を、試せる状態のまま残しておくのは、あとから同じ議論をやり直すときにとても助かりました。
丸くしたら、何が壊れたか
地面を丸くしたことで壊れたものは、このあとも続きました。スクロールで建物を巡るカメラの経路が南極に潜り込んだり、建物に寄ると看板が鏡文字になったり。その話は「看板が鏡文字になった日」に書いています。
平らな世界の常識は、コードのあちこちに染み込んでいました。この移行は、それを1つずつ見つけて、球の上でも成り立つ形に書き直していく作業の連続でした。
