モデリングなしで立体感を出す — 手で三角形を組む浮島

島の岩はモデリングツールから書き出したものではありません。五層の不規則なリングを実行時に組み上げ、頂点色とフラットシェーディングだけで石らしく見せています。

浮島の岩はモデリングツールも .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 の線形合同法から来ています。利点は二つ。ビルドのたびに同じ島になり、読者が「私が見た島」を共有できます。デバッグ中に更新するたび山が変わることもありません。

まとめ

手続き生成の難しさは数学ではなく取捨です。どの細部を頂点で表現し(岩層と明暗)、どれをインスタンス化に任せ(植生)、どれを二枚の円柱で済ませるか(芝生)。この三段を決めれば、四十行で一つの島になります。

← 記事一覧に戻る

コメント

…