Skip to content

three.js 下雪啦

Posted on:2022年5月11日 at 10:07

image

snow.js

import * as THREE from "three";

class Snow {
  #time = 0;
  #textureLoader = new THREE.TextureLoader();

  constructor(num = 0, range = 0, texture = "") {
    this.num = num;
    this.range = range;
    this.particle = null;
    this.texture = texture;
    this.texturesMaps = [];
    this.position = null;
    this.gen();
  }

  gen() {
    const { num, range, texture } = this;
    const geometry = new THREE.BufferGeometry();
    const material = new THREE.PointsMaterial({
      size: 6,
      transparent: true,
      opacity: 0.6,
      depthWrite: false,
      vertexColors: true,
      map: this.#textureLoader.load(texture),
      blending: THREE.AdditiveBlending, // 将黑色雪花与背景色融合
    });
    const position = []; // 每个粒子的位置
    const colors = [];
    for (let x = 0; x < num; x++) {
      // 采用HSL颜色模式使得雪花明暗度变化
      const color = new THREE.Color();
      const asHSL = { h: 0, s: 0, l: 0 };
      color.getHSL(asHSL);
      color.setHSL(asHSL.h, asHSL.s, asHSL.l * Math.random());
      colors.push(color.r, color.g, color.b);

      // 生成随机点坐标
      position.push(
        THREE.MathUtils.randFloatSpread(range * 2),
        THREE.MathUtils.randFloatSpread(range * 2),
        THREE.MathUtils.randFloatSpread(range * 2)
      );
    }
    this.position = new THREE.Float32BufferAttribute(position, 3);
    geometry.setAttribute("position", this.position);
    geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
    this.particle = new THREE.Points(geometry, material);
  }

  snowing(speed = 0, fps = 0) {
    this.#time += 1;
    if (fps > 0) {
      if (this.#time < fps * 60) return;
      this.#time = 0;
    }
    const { position, range } = this;
    for (let i = 0; i < position.count; i++) {
      let pos_x = position.getX(i);
      let pos_y = position.getY(i);
      let pos_z = position.getZ(i);

      pos_x -= speed;
      pos_y -= speed;
      pos_z -= speed;

      if (pos_x < -range) pos_x = range;
      if (pos_y < -range) pos_y = range;
      if (pos_z < -range) pos_z = range;

      position.setX(i, pos_x);
      position.setY(i, pos_y);
      position.setZ(i, pos_z);
    }
    // 更新 position
    position.needsUpdate = true;
  }
}

export default Snow;

index.js

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import Snow from "./snow";
import snowImg from "@/assets/textures/particles/snowflake1.png";
const { innerWidth: WIDTH, innerHeight: HEIGHT } = window;

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, WIDTH / HEIGHT, 0.1, 1000);
camera.position.set(0, 0, 160);
camera.lookAt(scene.position);

const ambientLight = new THREE.AmbientLight(0x666666);
scene.add(ambientLight);

const spotLight = new THREE.SpotLight(0xffffff);
spotLight.castShadow = true;
spotLight.position.set(-50, 80, 50);
scene.add(spotLight);

const snow = new Snow(10000, 100, snowImg);
scene.add(snow.particle);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

new OrbitControls(camera, renderer.domElement);
const render = () => {
  snow.snowing(0.3, 0.03);
  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

render();

附件 snowflake1.png

image