Three.js 实现超酷炫的火焰粒子效果!附完整源码

📸 效果预览

火焰效果

🚀 在线演示

👉 点击这里立即体验

🤔 前言

相信大家对火焰效果都不陌生,在游戏和影视中经常能看到逼真的火焰动画。

该实现基于 Three.js 和 WebGL 着色器,利用 Simplex 噪声算法模拟火焰的湍流效果,通过射线穿射采样实现体积渲染,最终呈现出非常逼真的火焰效果。

💻 核心代码解析

第一步:自定义火焰着色器材质

该实现的核心是一个继承自 THREE.ShaderMaterialFireMaterial 类:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FireMaterial extends THREE.ShaderMaterial {
  constructor() {
    super({
      defines: { ITERATIONS: '10', OCTIVES: '3' },
      uniforms: {
        fireTex: { type: 't', value: null },       // 火焰纹理
        time: { type: 'f', value: 0.0 },           // 时间
        seed: { type: 'f', value: 0.0 },           // 随机种子
        invModelMatrix: { type: 'm4', value: null }, // 逆模型矩阵
        scale: { type: 'v3', value: null },        // 缩放
        noiseScale: { type: 'v4', value: new THREE.Vector4(1, 2, 1, 0.3) },
        magnitude: { type: 'f', value: 2.5 },     // 湍流强度
        lacunarity: { type: 'f', value: 3.0 },     // 间隙度
        gain: { type: 'f', value: 0.6 }           // 增益
      },
      // ... 着色器代码
    })
  }
}

第二步:Simplex 噪声算法

片段着色器中实现了完整的 Simplex 3D 噪声函数,这是生成自然火焰效果的关键:

glsl
1
2
3
4
5
6
7
8
9
10
11
// Simplex噪声主函数
float snoise(vec3 v) {
  const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0);
  const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
  
  // 计算网格角点
  vec3 i = floor(v + dot(v, C.yyy));
  vec3 x0 = v - i + dot(i, C.xxx);
  
  // ... 计算其他角点和梯度
  // 返回归一化的噪声值
  return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3)));
}

第三步:湍流函数实现

通过多层噪声叠加产生复杂的湍流效果:

glsl
1
2
3
4
5
6
7
8
9
10
11
float turbulence(vec3 p) {
  float sum = 0.0;
  float freq = 1.0;
  float amp = 1.0;
  for(int i = 0; i < OCTIVES; i++) {
    sum += abs(snoise(p * freq)) * amp;
    freq *= lacunarity;    // 频率倍增
    amp *= gain;           // 振幅衰减
  }
  return sum;
}

第四步:火焰采样与射线穿射

核心渲染逻辑采用射线穿射采样技术:

glsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
  vec3 rayPos = vWorldPos;
  vec3 rayDir = normalize(rayPos - cameraPosition);
  float rayLen = 0.0288 * length(scale.xyz);
  vec4 col = vec4(0.0);
  
  // 多次采样累积颜色
  for(int i = 0; i < ITERATIONS; i++) {
    rayPos += rayDir * rayLen;
    vec3 lp = localize(rayPos);
    lp.y += 0.5;
    lp.xz *= 2.0;
    col += samplerFire(lp, noiseScale);
  }
  col.a = col.r;
  gl_FragColor = col;
}

第五步:Fire 类封装

将火焰逻辑封装为可复用的类:

javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Fire extends THREE.Mesh {
  constructor(scale = 7) {
    const geometry = new THREE.BoxGeometry();
    const material = new FireMaterial();
    super(geometry, material);
    
    this.scale.set(scale, scale, scale);
    this.material.transparent = true;
    this.material.depthWrite = false;
    this.material.depthTest = false;
  }
  
  update(time) {
    this.updateMatrixWorld();
    this.material.uniforms.invModelMatrix.value.copy(this.matrixWorld).invert();
    this.material.uniforms.time.value = time;
  }
}

🔍 关键技术点总结

技术点 作用 掌握难度
Simplex 噪声 生成自然的随机图案 ⭐⭐⭐
湍流函数 多层噪声叠加产生复杂效果 ⭐⭐⭐
射线穿射采样 实现体积渲染效果 ⭐⭐⭐
自定义着色器材质 完全控制渲染流程 ⭐⭐
透明混合 正确处理火焰透明度 ⭐⭐

🔧 常见问题

问题1:火焰看起来不够自然

原因:噪声参数设置不当,导致火焰形态过于规律

解决思路:调整 noiseScalemagnitudelacunaritygain 参数,找到合适的组合

问题2:性能较差

原因ITERATIONSOCTIVES 值过高,导致计算量过大

解决思路:降低迭代次数,或使用纹理噪声替代实时计算

🎁 完整源码

完整源码请参考在线演示页面,打开浏览器开发者工具即可查看。

📌 总结

通过这篇文章,我们学习了:

  • 如何使用 Simplex 噪声生成自然的火焰效果
  • 如何实现湍流函数模拟火焰的不规则运动
  • 如何使用射线穿射采样实现体积渲染
  • 如何封装自定义 Three.js 类

💡 扩展思路

如果你想让这个效果更酷炫,可以尝试:

  1. 添加火焰发光效果(使用 bloom 后期处理)
  2. 添加烟雾粒子系统配合火焰
  3. 实现火焰与场景物体的交互(如光照影响)
  4. 添加声音效果增强沉浸感

🙏 感谢

感谢你看到这里!如果这篇文章对你有帮助,别忘了点个赞👍,收藏一下⭐,转发给更多需要的人!

有什么问题,欢迎在评论区留言讨论~