React Three Fiberでシンプルなパーティクルアニメーションを実装してみた

React Three Fiberでシンプルなパーティクルアニメーションを実装してみた

ぼこ

ぼこ

こんにちは。フロントエンドエンジニアになりたてのぼこです(ついに2年目になってしまいましたが気持ち的には「なりたて」です)。

今回は、前回の記事で紹介したReact Three Fiberを使って、もう少し具体的な表現をしてみようということでパーティクルアニメーションの実装をしてみました。Webサイトのクリエイティブ表現といえばまずはパーティクル、みたいなとこありますよね(?)

コードの量は少なめにシンプルな演出にしたので、React Three Fiberを始めたての人にも参考になると嬉しいです。

React Three Fiberってなに?

React Three Fiber(以下r3f)とはなんぞ、についてはぜひ前回の記事を読んでいただけたらと思います。

ざっくりいうとReactでThreer.jsを扱うためのライブラリです。

デモの解説

今回作ったデモはこんな感じです。

複雑そうな形をしていますが、大まかにこんな流れで作っています。

  1. 板状にパーティクルを配置する
  2. ノイズでいい感じに動かす
  3. 真横から見る(結構寄りで)

板状にパーティクルを配置する

パーティクルを平面状にたくさん配置します。PlaneBufferGeometryから頂点座標を持ってきてそれを使うようにすると楽です。

const planePositions = useMemo(() => {
  const planeGeometry = new THREE.PlaneBufferGeometry(6, 6, 120, 120);
  const positions = planeGeometry.attributes.position.array;

  return positions;
}, []);

Reactの関数コンポーネントは何回も呼ばれるので、呼ばれるたびに毎回ジオメトリを作って頂点を取って〜としないようにメモ化します。

次にattributeの渡し方ですが、Three.jsだとgeometry.setAttributes()していきますが、r3fではこんな感じで書きます。Reactって感じですね。

<bufferGeometry attach="geometry">
  <bufferAttribute
    attachObject={['attributes', 'position']}
    array={planePositions}
    itemSize={3}
    count={planePositions.length / 3}
  />
</bufferGeometry>

attachObjectにはattributes変数であることを示す文字列と、シェーダー側での変数名を入力します。

arrayには頂点データの配列を入れ、itemSizeはデータが座標という三次元のデータなので3を入れます。

countは頂点数ですが、xyz座標が一次元の配列に入っているので配列の長さ / 3 になります。

ノイズでいい感じに動かす

シェーダーはThree.jsを使うときと変わりません。今回は直接JSファイルに文字列を書いてますが、実際にはGLSL用にファイルを分けて作ったりします。

// 頂点シェーダー
uniform float uTime;

void main() {
  float amp = 1.;
  float freq = 0.4;
  float time = uTime * 0.0008;

  float noiseValueX = snoise(-position * freq + time) * amp;
  float noiseValueY = snoise(position * freq + time) * amp;
  float noiseValueZ = snoise(position * freq + time) * amp;
  noiseValueZ += snoise(position * freq * 3. + time) * amp * 0.2;

  vec3 pos = vec3(position.x + noiseValueX, position.y + noiseValueY, position.z + noiseValueZ);

  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
  gl_PointSize =  2.0;
}

CodeSandboxの実際の頂点シェーダーはかなり長くなっていますが、9割が外部パッケージです。このシンプレックスノイズの関数はとても便利でよくお世話になっています。
webgl-noise

なんだかすごそうに見えますが、すごいのはシンプレックスノイズを発明した人とこのパッケージを提供している人で、僕は全然すごくありません。

// フラグメントシェーダー
void main() {
  // 点の中心からの距離を元に透明度を指定し、丸くする
  float alpha = 1. - smoothstep(0.4995, 0.5005, length(gl_PointCoord - vec2(0.5)));

  gl_FragColor = vec4(vec3(0.0), alpha);
}

Three.jsのPoints(点)はデフォルトでは四角形をしていますが、こんな感じで中心からの距離をもとにalpha値をいじってあげると丸くできます。

ShaderMaterialのこの辺りの設定をしないと縁が残ったりするので注意です。

transparent
depthTest={false}
depthWrite={false}

真横から見る(結構寄りで)

別に解説でもなんでもないですが、こういう波打っているような表現は、板状に配置されたパーティクルにノイズで動きを与えて、それを真横から結構寄りで見ると迫力があっていい感じに見えます。

ジェネラティブって感じでいいですね。

マウスドラッグでぐりぐり動かせるので、いろんな角度で見てみてください。

真上からだとただの板状なんだな〜というのがわかります。

余談ですが、このマウスでぐりぐり動かすためのおなじみOrbitControlsも、react-three/dreiという便利パッケーを使うことで簡単に導入できます(今回のCodeSandbox上ではパスを @react-three/drei/core/OrbitControls にしないと動かなかったんですが、本来は @react-three/drei で良いはずです)。

おわりに

今回はReact Three Fiberを使ってシンプルなパーティクルアニメーションを作ってみました。

これだけでもなかなか様になる絵ができるので、ここにさらに面白い動きやインタラクションを加えていけるといいですね。

これからも勉強していきます!

この記事のシェア数

44

ぼこ
ぼこ フロントエンドエンジニア / 藤田 侑己

新卒で入社しました。かまぼこのぼこです。その気になれば大抵のことはできると思っていますが、得意分野を聞かれると困るタイプです。LIGでそれが見つかったらいいなと思います。