Three.js 火焰材质教程 火焰材质 ·Fire Material· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么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循环中更新状态并 renderCesium 为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 开源仓库