モデリングなしで立体感を出す — 手で三角形を組む浮島
島の岩はモデリングツールから書き出したものではありません。五層の不規則なリングを実行時に組み上げ、頂点色とフラットシェーディングだけで石らしく見せています。
浮島の岩はモデリングツールも .glb も使っていません。五層の不規則なリングを実行時に組み上げるだけで、およそ四十行です。
まず島をリングの積み重ねとして考える
上から見れば円、横から見れば少しずつ小さく低くなるリングの積み重ねです。まず五層を決めます。
const rings = [
[radius * 0.94, 0.45],
[radius, -0.05],
[radius * 0.88, -depth * 0.28],
[radius * 0.64, -depth * 0.66],
[radius * 0.25, -depth],
];
各層を 19 分割し、角度は固定のまま半径と高さにわずかな揺らぎを加えます。揺らぎはごく小さく(半径 ±6%、高さ ±0.13)ですが、輪郭が完全な円でなくなるだけで「手続き生成に見える」印象が消えます。
手で三角化する、二重ループだけ
隣り合う二層と隣り合う二分割で四角形ができ、それを二つの三角形に割ります。インデックスバッファは使わず、頂点をそのまま配列に積みます。
for (let j = 0; j < rings.length - 1; j += 1) {
for (let i = 0; i < segments; i += 1) {
const a = ringPoints[j][i];
const b = ringPoints[j][(i + 1) % segments];
const c = ringPoints[j + 1][i];
const d = ringPoints[j + 1][(i + 1) % segments];
triangle(a, b, c, shades[Math.floor(random(0, shades.length))]);
triangle(b, d, c, shades[Math.floor(random(0, shades.length))]);
}
}
二つの三角形はそれぞれ違う明度を引きます。ここが一番費用対効果の高い行です。頂点色とフラットシェーディングで面ごとに法線が独立し、色もばらつくので、滑らかな曲面ではなく割れた岩の断面に見えます。
底面を忘れない
最後のリングを開けたままにすると、下から見たときに穴が空いて見えます。そこで三角形をもう一周足し、すべてを中心点に集めます。
triangle(last[i], last[(i + 1) % segments], vec(0, -depth * 1.12, 0), '#344b56');
中心点は最後のリングよりさらに低いので、島の底は円錐になります。浮島の直感と一致します。
芝生は二枚の円柱で足りる
岩の上に地形を作り直す必要はありません。平たい円柱を二枚重ねるだけです。下は草色で岩の上面より少し小さく、上は薄く淡い色で段の縁を作ります。その縁の色差が「ここに立てる」と最も雄弁に語ります。
低木、苔石、花はインスタンス化に任せます。役割は円柱の規則正しさを崩すことだけです。
かけらは同じ関数を使い回す
主島の外に浮かぶ三つのかけらは、同じ makeRock() を半径 0.48 から 0.77 で呼んでいます。同じ揺らぎを共有しているので、同じ地質に見えます。偶然ではなく、再利用が生む無料の一貫性です。
乱数は種を固定する
すべての random() は種 413 の線形合同法から来ています。利点は二つ。ビルドのたびに同じ島になり、読者が「私が見た島」を共有できます。デバッグ中に更新するたび山が変わることもありません。
まとめ
手続き生成の難しさは数学ではなく取捨です。どの細部を頂点で表現し(岩層と明暗)、どれをインスタンス化に任せ(植生)、どれを二枚の円柱で済ませるか(芝生)。この三段を決めれば、四十行で一つの島になります。

コメント
…