[Next.js #33] Interactive Noise Sphere — Three.js × GLSLで球体ノイズにクリック波紋を加える

はじめに

今回は、[Next.js #31] で作った “触れるノイズ場” の実装を、平面から球体へ拡張しました。

前回は PlaneGeometry に対して Raycaster で接触位置を取得し、GLSL 側でノイズ場へ局所的な反応を与える構成でした。 一方今回は、今朝公開した [Noise 入門 #32] の内容を踏まえ、SphereGeometry を使った球体ノイズ地形の上に、クリック位置から広がる波紋エフェクトを追加しています。

位置づけとしては、

  • 理論面: [Noise 入門 #32] の球体ノイズと 3D 座標ベースの地形変形
  • 実装面: [Next.js #31] の平面インタラクションの発展
  • 今回の着地: 球体表面を伝うクリック波紋デモ

という流れです。

なお、実行環境は Next.js プロジェクト内ですが、実装スタイルとしては React コンポーネント主体ではなく、public 配下で動かす ほぼバニラ Three.js + GLSL の構成です。 そのため今回は、Next.js の UI 実装というより、Next.js を足場にした Three.js / GLSL 実験の記録に近い内容になります。

前回のNext.js記事:

動画(PC):

今回作ったもの

今回のデモでは、次の要素を組み合わせました。

  • SphereGeometry を使った球体メッシュ
  • 3D ノイズによる球体地形の変形
  • Raycaster によるクリック位置の取得
  • 球面法線ベースのクリック波紋
  • 時間経過で外側へ広がるリング表現
  • 背景用のスカイスフィア

見た目としては、触ると反応するノイズ惑星 のような構成です。 単に球体にノイズを乗せるだけでなく、クリック位置を起点にしたインタラクションを加えることで、静的な地形表現から一歩進めた内容になりました。

前回との違い

[Next.js #31] では、平面メッシュ上で Raycaster の交点を取得し、その位置を shader に渡して局所的な反応を作っていました。

今回はそれをそのまま流用するのではなく、球体表面に合わせて考え方を変えています。

平面では、

  • 接触位置 = 平面ローカル座標
  • 距離 = distance(pos.xy, mouse.xy)

のように扱えます。

しかし球体では、単純な XY 距離では不自然になります。 そこで今回は、クリックした位置を球体ローカル座標へ変換し、さらに 法線方向として正規化 した上で、各頂点の法線方向との距離を使ってリングを作る形にしました。

つまり、

  • 平面版: ローカル平面距離
  • 球体版: 球面法線方向ベースの距離

という違いがあります。

この切り替えにより、クリック位置を中心に、球体表面を伝うようなリング表現が可能になります。

実装の流れ

1. 球体メッシュを用意する

ベースは SphereGeometry です。 分割数をある程度細かくしておくことで、頂点変形の変化が見やすくなります。

今回はここに対して、Vertex Shader 側で 3D ノイズを適用し、地形のような凹凸を作っています。

2. 3D ノイズで球体を変形する

球体では UV ベースでノイズを貼るより、法線方向ベースの 3D 座標ノイズ の方が自然です。

各頂点について、

  • 球の表面法線を求める
  • その方向ベクトルをノイズ座標として使う
  • FBM などで高さを計算する
  • 法線方向へ押し出す

という流れで、シームレスな球体地形を作れます。

このあたりの考え方自体は、今朝の [Noise 入門 #32] で整理した内容を、そのまま実装へ持ってきた形です。

3. Raycaster でクリック位置を取る

クリック時には Raycaster を使って球体との交点を取得します。

ただし、そのままワールド座標で扱うのではなく、いったん 球体ローカル座標へ変換し、そこから 法線方向として正規化 して保存します。

これによって、球体が回転していても「どの面をクリックしたか」を安定して扱いやすくなります。

4. 時間で広がるリングを作る

クリック波紋の考え方自体はシンプルです。

  • クリック時刻を保存する
  • 現在時刻との差分を取る
  • その経過時間に応じてリング半径を広げる
  • 頂点ごとの球面距離との差を使って帯状の影響を作る

こうすると、クリック地点から時間で広がるリング が作れます。

平面版で使っていた hover 反応とは違い、今回は 単発イベント + 時間変化 の構成です。 これにより、触れた瞬間の反応がより明確になり、視覚的にも分かりやすくなりました。

5. リング部分だけ色も持ち上げる

今回は頂点変形だけでなく、Fragment Shader 側でもリング部分を少し明るくしています。

こうしておくと、

  • 高さ変化
  • 色変化

の両方で波紋が見えるので、視認性が上がります。

特に球体地形側のノイズが強い場合、形状変化だけでは波紋が埋もれやすいので、色の補助はかなり有効でした。

6. 背景にスカイスフィアを追加する

黒背景だけでも成立はしますが、今回は背景として 大きな球体の内側に星空を描くスカイスフィア も追加しました。

実装としては、

  • 大きな SphereGeometry
  • THREE.BackSide
  • fragment shader 側で星点を描く

という形です。

最初は方向ベクトルをそのままセル分割していたため、星が四角いブロックのように見えてしまいました。 そこで、球面 UV ベースのセル分割に変え、セル内のランダム位置に小さな点を置く 方式に修正しました。

これで、背景として違和感の少ない星空になりました。

実装してみて分かったこと

今回の実装で面白かったのは、ノイズがなくても波紋自体は十分きれいに見える ことでした。

つまり今回の見た目の核は、

  • 3D ノイズ地形そのもの

ではなく、

  • 球面上を伝うリングの設計

にあります。

この確認ができたのは大きくて、今後は

  1. 球体波紋を主役にする
  2. ノイズ地形を弱く足す
  3. ノイズ地形を主役にして波紋を補助にする

といったバランス調整がしやすくなりました。

また、以前見た高水準な水面 shader 実装の影響もあり、 見た目を1つの shader に詰め込むのではなく、反応レイヤーとして分けて考える という発想もかなり参考になりました。

技術スタック

今回の実装で使っている主な要素は次の通りです。

  • Next.js(プロジェクト運用基盤)
  • Three.js(3D描画)
  • GLSL(頂点変形・波紋表現)
  • Raycaster(クリック位置取得)
  • lil-gui(パラメータ調整)

あらためて書くと、Next.js 製の 3D UI コンポーネントというより、 Next.js プロジェクトの中で運用しているバニラ Three.js 実験コード という立ち位置です。

今後の発展

今回の球体クリック波紋は、まだ単発リングが中心です。 今後広げるなら、たとえば次の方向があります。

  • 複数クリック履歴を保持して、複数リングを同時発生させる
  • リング通過後に余韻を残す
  • 波紋の形にノイズを混ぜる
  • 地形の高低に応じてリングの見え方を変える
  • 惑星ではなく生物的な球体表現へ寄せる

このあたりを追加していくと、単なるサンプルから、もう少し作品寄りの表現に発展させやすそうです。

まとめ

今回は、[Next.js #31] の平面インタラクション実装を土台にしつつ、[Noise 入門 #32] の球体ノイズの流れを取り込み、球体表面にクリック波紋が広がるデモ を作成しました。

平面から球体へ変わるだけで、

  • 接触位置の扱い
  • 距離の考え方
  • 波紋の見せ方

が変わり、実装としてもかなり面白い差が出ます。

Next.js の記事として見れば少し変則的ですが、中身としてはかなり素直な Three.js / GLSL の実験です。 今後もこの流れで、触れるノイズ表現 を平面から立体へ広げていきたいところです。