Three.js案例
下雨效果

用 Three.js 实现下雨效果

今天我们将使用 Three.js 来实现一个简单的下雨效果。先看一下效果

1. 创建基础场景

我们从一个简单的 Three.js 场景开始,创建一个渲染器、场景和相机。

// 创建场景
const scene = new THREE.Scene();
 
// 创建相机,参数为视角、纵横比、近裁剪面、远裁剪面
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
 
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

2. 创建一个地面

为了让下雨效果更加真实,我们可以添加一个地面(Plane)来接收雨滴的碰撞。

const geometry = new THREE.PlaneGeometry(1000, 1000);
const material = new THREE.MeshBasicMaterial({
  color: 0x0088ff,
  side: THREE.DoubleSide,
});
const ground = new THREE.Mesh(geometry, material);
ground.rotation.x = -Math.PI / 2; // 将地面旋转为水平
scene.add(ground);

3. 创建雨滴

我们将使用粒子系统来模拟雨滴,创建一个由大量小球组成的雨滴。

// 雨滴的粒子材质
const rainGeometry = new THREE.SphereGeometry(0.1, 8, 8);
const rainMaterial = new THREE.MeshBasicMaterial({ color: 0x88ccff });
 
// 创建一个数组来存储雨滴
const raindrops = [];
 
for (let i = 0; i < 1000; i++) {
  // 随机位置
  const x = Math.random() * 1000 - 500;
  const y = Math.random() * 1000 - 500;
  const z = Math.random() * 1000 - 500;
 
  // 创建雨滴并设置初始位置
  const raindrop = new THREE.Mesh(rainGeometry, rainMaterial);
  raindrop.position.set(x, y, z);
 
  // 将雨滴添加到数组中
  raindrops.push(raindrop);
  scene.add(raindrop);
}

4. 动画渲染雨滴

为了让雨滴看起来是“下落”的,我们需要在每一帧中更新雨滴的位置。这里我们简单地让雨滴的 Y 轴坐标不断减小,模拟下雨的效果。

function animate() {
  requestAnimationFrame(animate);
 
  // 更新雨滴位置
  raindrops.forEach((raindrop) => {
    raindrop.position.y -= 2;
 
    // 如果雨滴掉出场景,则重置位置
    if (raindrop.position.y < -500) {
      raindrop.position.y = 500;
    }
  });
 
  // 渲染场景
  renderer.render(scene, camera);
}
 
// 启动动画
animate();

5. 调整相机视角

为了使场景看起来更加生动,可以在适当的位置调整相机,确保我们可以看到下雨的效果。

// 调整相机位置
camera.position.z = 5;

6 添加简单的光源

虽然下雨通常是在阴天,我们可以添加一个简单的光源来提升效果的层次感。

// 创建环境光
const ambientLight = new THREE.AmbientLight(0x404040, 1); // 柔和的光
scene.add(ambientLight);
 
// 创建一个定向光(模拟太阳)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(0, 1, 1).normalize();
scene.add(directionalLight);

7. 响应窗口大小变化

为了确保当用户调整窗口大小时,场景能自适应,监听窗口尺寸变化并更新相机的宽高比和渲染器的大小。

window.addEventListener("resize", () => {
  renderer.setSize(window.innerWidth, window.innerHeight);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
});

8. 完整代码

将上述代码片段整合在一起,得到一个完整的下雨效果的 Three.js 示例:

import * as THREE from "three";
 
// 创建场景
const scene = new THREE.Scene();
 
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
 
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
 
// 创建地面
const geometry = new THREE.PlaneGeometry(1000, 1000);
const material = new THREE.MeshBasicMaterial({
  color: 0x0088ff,
  side: THREE.DoubleSide,
});
const ground = new THREE.Mesh(geometry, material);
ground.rotation.x = -Math.PI / 2;
scene.add(ground);
 
// 创建雨滴
const rainGeometry = new THREE.SphereGeometry(0.1, 8, 8);
const rainMaterial = new THREE.MeshBasicMaterial({ color: 0x88ccff });
const raindrops = [];
 
for (let i = 0; i < 5000; i++) {
  const x = Math.random() * 1000 - 500;
  const y = Math.random() * 1000 - 500;
  const z = Math.random() * 1000 - 500;
 
  const raindrop = new THREE.Mesh(rainGeometry, rainMaterial);
  raindrop.position.set(x, y, z);
  raindrops.push(raindrop);
  scene.add(raindrop);
}
 
// 创建光源
const ambientLight = new THREE.AmbientLight(0x404040, 1);
scene.add(ambientLight);
 
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(0, 1, 1).normalize();
scene.add(directionalLight);
 
// 调整相机位置
camera.position.z = 5;
 
// 动画函数
function animate() {
  requestAnimationFrame(animate);
 
  // 更新雨滴位置
  raindrops.forEach((raindrop) => {
    raindrop.position.y -= 2;
 
    // 如果雨滴掉出场景,则重置位置
    if (raindrop.position.y < -500) {
      raindrop.position.y = 500;
    }
  });
 
  renderer.render(scene, camera);
}
 
// 启动动画
animate();
 
// 监听窗口大小变化
window.addEventListener("resize", () => {
  renderer.setSize(window.innerWidth, window.innerHeight);
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
});

9. 总结

我们用 Three.js 创建了一个基本的下雨效果。这个过程包括:

  1. 设置基础的 Three.js 场景、相机和渲染器。
  2. 创建一个地面物体来展示雨滴的碰撞。
  3. 使用粒子系统(小球)模拟雨滴。
  4. 动态更新雨滴的位置以产生下落效果。
  5. 添加了简单的光源和响应式设计。