[JavaScript]Three.jsで雪、雨、クリスマスツリー、点滅するライトを実装

はじめに

今日は、Three.jsで、雨(雪)、クリスマスツリー周辺に点滅するライトを実装してみたのでその備忘録メモ。

PointsMaterialは、大量に作ってもパフォーマンスが落ちないようで、使えるマテリアルですね。

雨や雪以外に、宇宙空間に星空を作りたい場合などにも有効だと思います。

1. 雨(雪)の作成

1.1 雨エフェクトの実装

雨粒をパーティクルシステムとしてシミュレートする方法です。雨粒は、ランダムな位置に配置され、重力で落ちていきます。

const rainCount = 10000; // 雨の粒子数
const rainGeometry = new THREE.BufferGeometry();
const rainPositions = new Float32Array(rainCount * 3);

for (let i = 0; i < rainCount; i++) {
  rainPositions[i * 3] = Math.random() * 500 - 250;   // X座標
  rainPositions[i * 3 + 1] = Math.random() * 500 - 250; // Y座標
  rainPositions[i * 3 + 2] = Math.random() * 500 - 250; // Z座標
}

rainGeometry.setAttribute('position', new THREE.BufferAttribute(rainPositions, 3));

const rainMaterial = new THREE.PointsMaterial({
  color: 0xaaaaaa,  // 灰色の雨
  size: 0.1,
  opacity: 0.8,
  transparent: true,
});

const rain = new THREE.Points(rainGeometry, rainMaterial);
scene.add(rain);

function updateRain() {
  const positions = rainGeometry.attributes.position.array;
  for (let i = 0; i < positions.length; i += 3) {
    positions[i + 1] -= 0.5;  // Y座標を減少させて雨粒が落ちる
    if (positions[i + 1] < -250) positions[i + 1] = 250;  // 地面に当たったら上に戻す
  }

  rainGeometry.attributes.position.needsUpdate = true;
}

1.2 雪エフェクトの実装

雪も同様にパーティクルシステムを使用して、ランダムに降らせるエフェクトを作成します。

const snowCount = 10000; // 雪の粒子数
const snowGeometry = new THREE.BufferGeometry();
const snowPositions = new Float32Array(snowCount * 3);

for (let i = 0; i < snowCount; i++) {
  snowPositions[i * 3] = Math.random() * 500 - 250;   // X座標
  snowPositions[i * 3 + 1] = Math.random() * 500 - 250; // Y座標
  snowPositions[i * 3 + 2] = Math.random() * 500 - 250; // Z座標
}

snowGeometry.setAttribute('position', new THREE.BufferAttribute(snowPositions, 3));

const snowMaterial = new THREE.PointsMaterial({
  color: 0xffffff,  // 白色の雪
  size: 0.3,
  opacity: 0.7,
  transparent: true,
});

const snow = new THREE.Points(snowGeometry, snowMaterial);
scene.add(snow);

function updateSnow() {
  const positions = snowGeometry.attributes.position.array;
  for (let i = 0; i < positions.length; i += 3) {
    positions[i + 1] -= 0.5;  // Y座標を減少させて雪粒が落ちる
    if (positions[i + 1] < -250) positions[i + 1] = 250;  // 地面に当たったら上に戻す
  }

  snowGeometry.attributes.position.needsUpdate = true;
}

2. クリスマスツリーの作成

当初は、自分でジオメトリを組み合わせて作ろうかと思いましたが、ちょっと味気なかったので、

また、https://sketchfab.com/ から素材をお借りしてきました。

以下は、MeshBasicMaterialを使った当初の実装サンプルです。

2.1 ツリーの作成

THREE.ConeGeometryを使用して、円錐型のクリスマスツリーを作成します。ツリーの上には星を置いて、完成度を高めます。

function createChristmasTree() {
  const treeHeight = 10;
  const treeGeometry = new THREE.ConeGeometry(5, treeHeight, 8);  // 円錐形
  const treeMaterial = new THREE.MeshBasicMaterial({ color: 0x228B22 });  // 緑色
  const tree = new THREE.Mesh(treeGeometry, treeMaterial);
  tree.position.set(0, treeHeight / 2, 0);  // 地面に立つように配置
  scene.add(tree);

  // ツリーの上に星を追加
  const starGeometry = new THREE.SphereGeometry(1, 32, 32);
  const starMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 });  // 黄色
  const star = new THREE.Mesh(starGeometry, starMaterial);
  star.position.set(0, treeHeight, 0);  // ツリーの頂上に配置
  scene.add(star);
}

3. ポイントライトによるライトアップと点滅

3.1 ポイントライトを追加

ツリーを光らせるために、THREE.PointLight を使ってLEDライトをツリーに配置します。ここで、複数のライトをランダムに配置し、点滅させます。

function createLEDLights() {
  const lightCount = 100;  // LEDの数
  for (let i = 0; i < lightCount; i++) {
    const x = Math.random() * 10 - 5;
    const y = Math.random() * 10 + 2;
    const z = Math.random() * 10 - 5;

    const color = Math.random() < 0.5 ? 0xff0000 : 0x00ff00;  // 赤か緑
    const light = new THREE.PointLight(color, 1, 10);
    light.position.set(x, y, z);
    scene.add(light);
  }
}

3.2 ライトの点滅

ライトが点滅するように、時間に応じてライトの強さを変更します。Math.sinを使って、点滅効果をシミュレートします。

function updateLEDLights() {
  scene.children.forEach(child => {
    if (child instanceof THREE.PointLight) {
      child.intensity = Math.sin(Date.now() * 0.005) * 0.5 + 0.5;  // 点滅する強さ
    }
  });
}

4. アニメーションループ

上記のエフェクトを全て組み合わせるために、アニメーションループを使って定期的に更新を行います。

function animate() {
  requestAnimationFrame(animate);  // 次のフレームを呼び出し

  updateRain();  // 雨の更新
  updateSnow();  // 雪の更新
  updateLEDLights();  // LEDライトの点滅

  renderer.render(scene, camera);  // シーンをレンダリング
}

animate();  // アニメーション開始

5. まとめ

  • 雨(雪)のエフェクト: THREE.BufferGeometryTHREE.PointsMaterial を使って、雨や雪の粒子を表現。時間経過に応じて動かす。
  • クリスマスツリー: THREE.ConeGeometry を使用してツリーを作成し、THREE.SphereGeometry で星を追加。
  • ライトアップ: THREE.PointLight を使ってツリーの周りに複数のLEDライトを追加し、点滅させる。
  • アニメーション: requestAnimationFrame とアニメーションループで、動きのあるシーンを作成。

これで、クリスマスの雰囲気を盛り上げるシーンが完成しました!最適化や追加のエフェクトを試して、さらにクオリティを高めることもできます。