news 2026/7/7 8:35:47

Three.js 火焰材质教程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Three.js 火焰材质教程

火焰材质 ·Fire Material· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • ShaderMaterial 自定义着色器实现核心视觉效果
  • OrbitControls 相机轨道交互
  • requestAnimationFrame渲染循环与resize自适应

效果说明

本案例演示火焰材质效果:基于 WebGL 实现「火焰材质」可视化效果,附完整可运行源码;核心用到 ShaderMaterial、OrbitControls。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Scene / Camera / WebGLRenderer构成最小渲染闭环;大场景可开logarithmicDepthBuffer缓解 Z-fighting。
  • ShaderMaterial通过uniforms+ 自定义 GLSL 控制逐像素/逐点效果;透明粒子常配合depthTest: false
  • OrbitControls提供轨道旋转/缩放;开启enableDamping后需在 animate 中controls.update()

实现步骤

  • 搭建 Scene、PerspectiveCamera、WebGLRenderer,挂载 canvas 并处理resize
  • 定义 uniforms / onBeforeCompile 或 ShaderMaterial,编写 GLSL 与材质参数
  • 创建 OrbitControls(及 Raycaster 等交互控件,若源码包含)
  • requestAnimationFrame循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as THREE from 'three';

    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

    // 保持FireMaterial类不变 class FireMaterial extends THREE.ShaderMaterial { constructor() { super({ defines: { ITERATIONS: '10', OCTIVES: '3' }, uniforms: { fireTex: { type: 't', value: null }, color: { type: 'c', 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 } }, vertexShader:varying vec3 vWorldPos; void main() { gl_Position = projectionMatrixmodelViewMatrixvec4(position, 1.0); vWorldPos = (modelMatrix * vec4(position, 1.0)).xyz; }, fragmentShader:// 注意:这里我们直接内联noise函数,替代原先的glsl-noise导入 // Simplex 3D noise function vec3 mod289(vec3 x) { return x - floor(x(1.0 / 289.0))289.0; } vec4 mod289(vec4 x) { return x - floor(x(1.0 / 289.0))289.0; } vec4 permute(vec4 x) { return mod289(((x34.0) + 1.0)x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } 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); // First corner vec3 i = floor(v + dot(v, C.yyy)); vec3 x0 = v - i + dot(i, C.xxx); // Other corners vec3 g = step(x0.yzx, x0.xyz); vec3 l = 1.0 - g; vec3 i1 = min(g.xyz, l.zxy); vec3 i2 = max(g.xyz, l.zxy); vec3 x1 = x0 - i1 + C.xxx; vec3 x2 = x0 - i2 + C.yyy; vec3 x3 = x0 - D.yyy; // Permutations i = mod289(i); vec4 p = permute(permute(permute( i.z + vec4(0.0, i1.z, i2.z, 1.0))

    • i.y + vec4(0.0, i1.y, i2.y, 1.0))
      • i.x + vec4(0.0, i1.x, i2.x, 1.0));
    // Gradients: 7x7 points over a square, mapped onto an octahedron. float n_ = 0.142857142857; vec3 ns = n_ * D.wyz - D.xzx; vec4 j = p - 49.0floor(pns.z * ns.z); vec4 x_ = floor(j * ns.z); vec4 y_ = floor(j - 7.0 * x_); vec4 x = x_ * ns.x + ns.yyyy; vec4 y = y_ * ns.x + ns.yyyy; vec4 h = 1.0 - abs(x) - abs(y); vec4 b0 = vec4(x.xy, y.xy); vec4 b1 = vec4(x.zw, y.zw); vec4 s0 = floor(b0) * 2.0 + 1.0; vec4 s1 = floor(b1) * 2.0 + 1.0; vec4 sh = -step(h, vec4(0.0)); vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; vec3 p0 = vec3(a0.xy, h.x); vec3 p1 = vec3(a0.zw, h.y); vec3 p2 = vec3(a1.xy, h.z); vec3 p3 = vec3(a1.zw, h.w); // Normalise gradients vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; // Mix final noise value vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); m = m * m; return 42.0dot(mm, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); }

    uniform vec3 color; uniform float time; uniform float seed; uniform mat4 invModelMatrix; uniform vec3 scale; uniform vec4 noiseScale; uniform float magnitude; uniform float lacunarity; uniform float gain; uniform sampler2D fireTex; varying vec3 vWorldPos;

    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(pfreq))amp; freq *= lacunarity; amp *= gain; } return sum; }

    vec4 samplerFire (vec3 p, vec4 scale) { vec2 st = vec2(sqrt(dot(p.xz, p.xz)), p.y); if(st.x <= 0.0 || st.x >= 1.0 || st.y <= 0.0 || st.y >= 1.0) return vec4(0.0); p.y -= (seed + time) * scale.w; p *= scale.xyz; st.y += sqrt(st.y)magnitudeturbulence(p); if(st.y <= 0.0 || st.y >= 1.0) return vec4(0.0); return texture2D(fireTex, st); }

    vec3 localize(vec3 p) { return (invModelMatrix * vec4(p, 1.0)).xyz; }

    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类来替代React组件 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; // 初始化uniform值 this.material.uniforms.invModelMatrix.value = new THREE.Matrix4(); this.material.uniforms.scale.value = this.scale; this.material.uniforms.seed.value = Math.random() * 19.19; this.material.uniforms.color.value = new THREE.Color(0xeeeeee); } update(time) { // 更新材质 this.updateMatrixWorld(); this.material.uniforms.invModelMatrix.value.copy(this.matrixWorld).invert(); this.material.uniforms.time.value = time; }

    setTexture(texture) { texture.magFilter = texture.minFilter = THREE.LinearFilter; texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping; this.material.uniforms.fireTex.value = texture; } }

    // 初始化场景 function init() { // 创建场景 const scene = new THREE.Scene(); // 创建相机 const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.set(0, -4, 5); // 创建渲染器 const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 添加轨道控制 const controls = new OrbitControls(camera, renderer.domElement); // 创建火焰 const fire = new Fire(7); scene.add(fire); // 加载纹理 const textureLoader = new THREE.TextureLoader(); textureLoader.load(HOST + 'files/images/fire.png', (texture) => { fire.setTexture(texture); }); // 动画循环 const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const time = clock.getElapsedTime(); fire.update(time); renderer.render(scene, camera); } // 处理窗口大小变化 window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // 开始动画 animate(); return { scene, camera, renderer, fire }; }

    // 启动应用 window.addEventListener('DOMContentLoaded', init);

    export { FireMaterial, Fire, init };

    完整源码:GitHub

    小结

    • 本文提供火焰材质完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 8:34:33

5个理由让你爱上SFML:跨平台多媒体开发的终极解决方案

5个理由让你爱上SFML&#xff1a;跨平台多媒体开发的终极解决方案 【免费下载链接】SFML Simple and Fast Multimedia Library 项目地址: https://gitcode.com/gh_mirrors/sf/SFML SFML&#xff08;Simple and Fast Multimedia Library&#xff09;是一个简单快速的跨平…

作者头像 李华
网站建设 2026/7/7 8:34:27

CLIP 超通俗讲解

CLIP 超通俗讲解 1 CLIP 一句话看懂 CLIP 全称 Contrastive Language-Image Pre-training(对比语言图像预训练),OpenAI 2021年提出的跨模态基石模型。 通俗类比 把图片、文字翻译成同一种“数字语言(向量)”,放进同一个球形空间: 语义匹配的图和文字 → 向量挨得近(…

作者头像 李华
网站建设 2026/7/7 8:32:48

酒店语音客控系统实战AI音箱与语音面板部署全流程

语音交互正在成为酒店智能化的重要入口。根据迈点研究院2025年数据&#xff0c;超过65%的中高端酒店住客期望通过语音控制客房设备&#xff0c;而这一比例在经济型酒店中仅为28%&#xff08;来源&#xff1a;迈点研究院&#xff0c;《2025中国酒店住客体验报告》&#xff0c;20…

作者头像 李华
网站建设 2026/7/7 8:32:15

低成本私域方案:如何为中小业务自研轻量级微信聊天机器人?

在很多中小型电商、本地生活或者创业团队的私域运营场景中&#xff0c;技术团队经常会接到这样的需求&#xff1a;既需要给刚加的好友自动发个欢迎语&#xff0c;又需要根据客户发来的关键词&#xff08;比如“地址”、“营业时间”、“价格表”&#xff09;进行智能回复。 对…

作者头像 李华
网站建设 2026/7/7 8:31:51

华为ModelArts:AI开发全流程利器

&#x1f3af; 平台核心定位与价值ModelArts平台的核心使命是简化AI开发&#xff0c;它通过以下方式实现这一目标&#xff1a;全流程覆盖&#xff1a;提供从数据管理、开发环境、模型训练、模型管理到部署上线的完整工具链。降低门槛&#xff1a;无论是零代码的自动学习&#x…

作者头像 李华
网站建设 2026/7/7 8:30:42

华硕笔记本性能优化终极指南:G-Helper让你的电脑重获新生

华硕笔记本性能优化终极指南&#xff1a;G-Helper让你的电脑重获新生 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook,…

作者头像 李华