THREE.js 下雪效果
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: .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, .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(.3, .03)
requestAnimationFrame(render)
renderer.render(scene, camera)
}
render()
附件 snowflake1.png
为之则易,不为则难。