[JavaScript] Three.jsでMarchingCubes(Metaballs)を箱庭に組み込んで動かしてみた

はじめに

ネットのサンプルを見ていると、球体メッシュ同士が近づくとくっついて1つの物体になったり、離れると分離するというアニメーションをたまに見ますが、あれを実装してみました。

見た目は凄そうなのですが、実際はそんなに難しくなく、Three.jsにあらかじめ用意されている、 Marching Cubes を利用するだけで比較的簡単に実装できます。

今回も、開発中のゲームの箱庭空間内に水槽を作り、その中に Marching Cubes 本体入れてバウンドさせるというシンプルなアニメーションを実装してみたのでそのメモです。

単なるアニメーションでは面白くないので、水槽の中に自キャラが入ってMarchingCubesに接触すると反応する所まで作りこんでみました。

動画(パソコン)

動画(VR)

追記:

翌朝、AIに聞きつつシェーダーを弄って見ましたが、難しいので挫折。修行が足りない。

MarchingCubes は Mesh ?

MarchingCubes は見た目は Mesh と同じで、

  • scene.add() できる
  • position / scale を持つ

ただし中身は全く違う。

形状は固定されておらず、毎フレーム

reset()
addBall()
update()

で 内部のボリュームからメッシュを再生成している。

この update() を呼ばないと、 どれだけ addBall() をしても 何も表示されない。

水槽(透明Box)と組み合わせる

MarchingCubes 単体だと空間的な境界が分かりにくいので、 透明な Box を作って「水槽」っぽくした。

const tank = new THREE.Mesh(
  new THREE.BoxGeometry(3.6, 2.0, 2.6),
  new THREE.MeshStandardMaterial({
    color: 0xffffff,
    transparent: true,
    opacity: 0.5,
    depthWrite: false,
  })
);

depthWrite: false を入れないと、

  • 水槽の中に入った瞬間
  • Metaballs が見えなくなる

という挙動になる。

reset → addBall → update

MarchingCubes は 状態を保持しない。

毎フレーム:

  1. reset() で世界を全消去
  2. addBall() で「このフレームに存在する影響源」を登録
  3. update() で形状を再構築

addBall() を1つでも呼ばなければ、そのフレームでは何も存在しない。

world座標はそのまま使えない

MarchingCubes の座標は 0〜1 の正規化空間。

そのため、箱庭の world 座標をそのまま渡しても一切反応しない。

そこで、水槽を基準に world → 0..1 に変換する関数を用意した。

function worldToMetaballSpace(worldPos, metaballs) {
  const size = metaballs.scale.x;
  const min = metaballs.position.clone().addScalar(-size / 2);
  const max = metaballs.position.clone().addScalar(size / 2);

  return new THREE.Vector3(
    (worldPos.x - min.x) / (max.x - min.x),
    (worldPos.y - min.y) / (max.y - min.y),
    (worldPos.z - min.z) / (max.z - min.z)
  );
}

ログを出してみると、

  • 0〜1 に入っていない座標は
  • エラーも出ず、完全に無視される

挙動は癖が強い

プレイヤー位置を Metaball として追加すると、

  • 歪むというより
  • 別の塊が出現して分離する

という挙動になった。

これはバグではなく、

  • 影響源が増えた
  • 等値面が再計算された

結果。

MarchingCubes は「既存形状を変形する」のではなく、 毎回、全体を作り直すという思想だと分かる。

触ってみて分かったこと

  • MarchingCubes は技術的には面白い
  • でも世界と自然に結びつけるのは難しい
  • ゲームロジック向きではない

一方で、

  • 独立したオブジェクト
  • 実験用演出
  • 観賞用オブジェクト

としてはかなり強力。