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