This commit is contained in:
2025-11-21 13:32:00 +08:00
parent 4e3455a5b6
commit e047368930
2 changed files with 169 additions and 10 deletions

View File

@@ -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,
}
}

View File

@@ -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 })
})