From e0473689308bed89377564488fdf5927184c191a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=20=E7=9B=B8=E5=8D=BF?= Date: Fri, 21 Nov 2025 13:32:00 +0800 Subject: [PATCH] demos --- src/composes/three/particles.ts | 125 ++++++++++++++++++++++++++++++++ src/views/three/12/index.vue | 54 +++++++++++--- 2 files changed, 169 insertions(+), 10 deletions(-) diff --git a/src/composes/three/particles.ts b/src/composes/three/particles.ts index fa81636..1447e50 100644 --- a/src/composes/three/particles.ts +++ b/src/composes/three/particles.ts @@ -278,6 +278,130 @@ export function useParticleSystem() { } } + /** + * 创建增强型火焰发射器 - 用于大型中心火焰效果 + */ + function createIntenseFireEmitter( + scene: THREE.Scene, + position: THREE.Vector3, + emissionRate = 50, + spreadRadius = 2, + ) { + const texture = createFlameTexture() + const particles: Particle[] = [] + let emissionCounter = 0 + + function emit() { + for (let i = 0; i < emissionRate; i++) { + const particlePos = position.clone() + + // 在圆形区域内随机分布 + const angle = Math.random() * Math.PI * 2 + const radius = Math.random() * spreadRadius + particlePos.x += Math.cos(angle) * radius + particlePos.z += Math.sin(angle) * radius + + // 多样化的火焰颜色 + const colorVariation = Math.random() + let color: number + if (colorVariation < 0.3) { + color = 0xFFFFFF // 白色(最热) + } + else if (colorVariation < 0.6) { + color = 0xFFDD00 // 黄色 + } + else if (colorVariation < 0.85) { + color = 0xFF8800 // 橙色 + } + else { + color = 0xFF2200 // 红色 + } + + // 粒子大小和速度变化 + const sizeVariation = 0.3 + Math.random() * 0.5 + const speedVariation = 0.05 + Math.random() * 0.1 + + const particle = createParticle(texture, particlePos, { + color, + opacity: 0.6 + Math.random() * 0.3, + size: sizeVariation, + velocity: new THREE.Vector3( + (Math.random() - 0.5) * 0.08, + speedVariation, + (Math.random() - 0.5) * 0.08, + ), + maxAge: 40 + Math.floor(Math.random() * 30), + }) + scene.add(particle.sprite) + particles.push(particle) + } + } + + function update() { + // 更新现有粒子 + for (let i = particles.length - 1; i >= 0; i--) { + const particle = particles[i] + if (!particle) + continue + + // 自定义更新逻辑以增强视觉效果 + particle.sprite.position.add(particle.velocity) + + // 添加轻微的湍流效果 + particle.velocity.x += (Math.random() - 0.5) * 0.002 + particle.velocity.z += (Math.random() - 0.5) * 0.002 + + particle.age++ + + // 更新大小和透明度 + const lifeRatio = particle.age / particle.maxAge + const initialSize = 0.3 + // 火焰先变大后缩小 + const sizeFactor = Math.sin(lifeRatio * Math.PI) * 2 + 1 + particle.sprite.scale.setScalar(initialSize * sizeFactor) + + // 透明度渐变 + particle.sprite.material.opacity = 0.8 * (1 - lifeRatio * lifeRatio) + + // 颜色渐变(从亮到暗) + const material = particle.sprite.material as THREE.SpriteMaterial + if (lifeRatio > 0.5) { + const darkening = (lifeRatio - 0.5) * 2 + material.color.lerp(new THREE.Color(0x440000), darkening * 0.5) + } + + const shouldRemove = particle.age >= particle.maxAge + + if (shouldRemove) { + scene.remove(particle.sprite) + particle.sprite.material.dispose() + particles.splice(i, 1) + } + } + + // 控制发射频率 + emissionCounter++ + if (emissionCounter >= 1) { + emit() + emissionCounter = 0 + } + } + + function stop() { + particles.forEach((particle) => { + scene.remove(particle.sprite) + particle.sprite.material.dispose() + }) + particles.length = 0 + } + + return { + update, + stop, + particles, + } + } + return { createSmokeTexture, createFlameTexture, @@ -285,5 +409,6 @@ export function useParticleSystem() { updateParticle, createEmitter, createFlameEmitter, + createIntenseFireEmitter, } } diff --git a/src/views/three/12/index.vue b/src/views/three/12/index.vue index e22895c..8bb446c 100644 --- a/src/views/three/12/index.vue +++ b/src/views/three/12/index.vue @@ -304,6 +304,19 @@ function spreadFire() { } } +// 移除初始着火点的火焰 +function removeInitialFire() { + // 找到并移除 pingt01_0019_pCylinder10001 的火焰发射器 + for (let i = flameEmitters.length - 1; i >= 0; i--) { + const { emitter, mesh } = flameEmitters[i] + if (mesh && mesh.name === 'pingt01_0019_pCylinder10001') { + emitter.stop() + flameEmitters.splice(i, 1) + break + } + } +} + // 创建中心大火效果 function createCenterFire() { if (!model) @@ -311,6 +324,9 @@ function createCenterFire() { const particleUtils = useParticleSystem() + // 先移除初始着火点 + removeInitialFire() + // 移除所有小火焰发射器 flameEmitters.forEach(({ emitter }) => { emitter.stop() @@ -323,23 +339,41 @@ function createCenterFire() { const modelCenter = modelBox.getCenter(new THREE.Vector3()) const modelSize = modelBox.getSize(new THREE.Vector3()) - // 在中心创建大型火焰发射器 - centerFireEmitter = particleUtils.createFlameEmitter( + // 在中心创建大型火焰发射器 - 使用增强版 + centerFireEmitter = particleUtils.createIntenseFireEmitter( scene, - new THREE.Vector3(modelCenter.x, modelBox.min.y + modelSize.y * 0.3, modelCenter.z), - 30, // 大量粒子 + new THREE.Vector3(modelCenter.x, modelBox.min.y, modelCenter.z), + 80, // 更多粒子 + modelSize.x * 0.5, // 扩散范围 ) - // 添加多个位置的火焰,模拟整体燃烧 + // 添加多层火焰效果,模拟整体燃烧 + const layers = [ + { y: modelBox.min.y, count: 60, spread: modelSize.x * 0.4 }, + { y: modelBox.min.y + modelSize.y * 0.3, count: 50, spread: modelSize.x * 0.3 }, + { y: modelBox.min.y + modelSize.y * 0.6, count: 40, spread: modelSize.x * 0.2 }, + ] + + layers.forEach((layer) => { + const emitter = particleUtils.createIntenseFireEmitter( + scene, + new THREE.Vector3(modelCenter.x, layer.y, modelCenter.z), + layer.count, + layer.spread, + ) + flameEmitters.push({ emitter, mesh: null }) + }) + + // 四周添加火焰柱 const positions = [ - new THREE.Vector3(modelBox.min.x, modelBox.min.y + 1, modelCenter.z), - new THREE.Vector3(modelBox.max.x, modelBox.min.y + 1, modelCenter.z), - new THREE.Vector3(modelCenter.x, modelBox.min.y + 1, modelBox.min.z), - new THREE.Vector3(modelCenter.x, modelBox.min.y + 1, modelBox.max.z), + new THREE.Vector3(modelBox.min.x, modelBox.min.y, modelCenter.z), + new THREE.Vector3(modelBox.max.x, modelBox.min.y, modelCenter.z), + new THREE.Vector3(modelCenter.x, modelBox.min.y, modelBox.min.z), + new THREE.Vector3(modelCenter.x, modelBox.min.y, modelBox.max.z), ] positions.forEach((pos) => { - const emitter = particleUtils.createFlameEmitter(scene, pos, 20) + const emitter = particleUtils.createIntenseFireEmitter(scene, pos, 40, modelSize.x * 0.15) flameEmitters.push({ emitter, mesh: null }) })