👻

threejsでperlin noiseを使用した球の作り方

に公開

以下のような球をthreejsで作成する方法をまとめました。

perlin noiseとは

コンピュータグラフィックスで自然なテクスチャやパターンを生成するために用いられるノイズ関数の一種。隣接する値が滑らかに変化するため、雲や地形、炎、水面など、自然物の表現に活用される。(Google AIによる要約より)

と、上記にありますがこれだけでは理解が難しいため、noiseとrandomの違いを通して理解していきます。

noiseとrandomの違い

noise:入力となる値が近い場合、出力される値も近くなる。
random:完全にランダム。規則性なし。

以下の例では、xの位置を引数として取得したノイズ値とランダム値を線の長さとして描画したものです。ノイズ値を使用して描画した方が滑らかに変化していることが確認できます。

ノイズ値を線の長さとした場合

ランダム値を線の長さとした場合

上記にある隣接する値が滑らかに変化するについて、今回の場合、隣接する値はxの位置のため、xの位置が近い線の長さが滑らかに変化していることが確認できます。この性質を利用することでさまざまな表現が可能となります。

perlin noiseを使用して球を動かす

stepを分けて説明します。

step1 meshの作成

まずはnoiseを適用する対象となるmeshを作成します。meshはgeometryとmaterialから構成されます。geometryは形状(立方体、球など)、materialは見た目(色、質感など)を表します。
球を作成するには以下のようにします。

sim.js
  const sphereRadius = 4;
  const sphereGeometry = new THREE.SphereGeometry(sphereRadius, 128, 128);
  const sphereMaterial = new THREE.MeshBasicMaterial({
    color: 0xcccccc,
    wireframe: true,
  });
  const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);

step2 頂点情報の取得

各頂点を入力としてノイズ値を取得するため、step1で作成したmeshの頂点情報を以下のように取得します。

sim.js
  const vertices = sphere.geometry.attributes.position.array;

step3 各頂点をノイズ値を使用して動かす

ここが一番重要な箇所となります。まず初めに球の頂点を更新するコードを以下に示します。

sim.js
  function noiseUpdate() {
    const time = performance.now() * 0.001;
    const k = 2;
    const normalEase = 0.25;

    // 拡張点に対してperlin noiseを適用
    for (let i = 0; i < vertices.length; i++) {
      const p = new THREE.Vector3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
      p.normalize().multiplyScalar(1 + normalEase * noise.perlin3(p.x * k + time, p.y * k, p.z * k));
      vertices[i * 3] = p.x * sphereRadius;
      vertices[i * 3 + 1] = p.y * sphereRadius;
      vertices[i * 3 + 2] = p.z * sphereRadius;
    }
    sphere.geometry.attributes.position.needsUpdate = true;
  }

コードについて解説していきます。

まずはじめに、以下ではste2で取得した頂点情報をベクトルとして定義しています。

sim.js
const p = new THREE.Vector3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);

次に単位ベクトルに対してスカラーを掛けることで球を動かします。

sim.js
p.normalize().multiplyScalar(1 + normalEase * noise.perlin3(p.x * k + time, p.y * k, p.z * k));

normalizeで単位ベクトルに変換後、multiplyScalarにノイズ値を適用することで伸縮させています。
ノイズ値を取得する際に使用する値は各頂点の位置(x, y, z)のため、近い頂点のノイズ値は似たような値となります。

最後にベクトルに対して半径をかけることで頂点の最終的な位置を求めます。

sim.js
vertices[i * 3] = p.x * sphereRadius;
vertices[i * 3 + 1] = p.y * sphereRadius;
vertices[i * 3 + 2] = p.z * sphereRadius;

sphere.geometry.attributes.position.needsUpdate = true; // 頂点座標の更新を通知する

threejsでperlin noiseを使用した球の作り方は以上となります。

今回作成したソースコードは以下から確認できます。
https://github.com/fejwy57hg/my-technical-submissions/tree/main/perlin niose

noiseの生成には以下を使用しました。
https://github.com/josephg/noisejs/tree/master

noiseの解説は以下の動画が非常に分かりやすかったです。
https://www.youtube.com/watch?v=Qf4dIN99e2w&list=PLRqwX-V7Uu6bgPNQAdxQZpJuJCjeOr7VD

Discussion