1. 程式人生 > 其它 >THREE.js 下雪效果

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