Three.js 蜡烛教程
蜡烛 ·Candle Shader· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互CatmullRomCurve3 样条曲线路径requestAnimationFrame渲染循环与resize自适应效果说明本案例演示蜡烛效果基于 WebGL 实现「蜡烛」可视化效果附完整可运行源码核心用到 ShaderMaterial、OrbitControls、CatmullRomCurve3。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Scene / Camera / WebGLRenderer构成最小渲染闭环大场景可开logarithmicDepthBuffer缓解 Z-fighting。ShaderMaterial通过uniforms 自定义 GLSL 控制逐像素/逐点效果透明粒子常配合depthTest: false。OrbitControls提供轨道旋转/缩放开启enableDamping后需在 animate 中controls.update()。曲线类getPoints(n)将贝塞尔/样条离散为路径点再写入 BufferGeometry 驱动飞线或路径动画。实现步骤搭建 Scene、PerspectiveCamera、WebGLRenderer挂载 canvas 并处理resize定义 uniforms / onBeforeCompile 或 ShaderMaterial编写 GLSL 与材质参数用曲线离散点构建 BufferGeometry写入自定义 attribute 驱动动画创建 OrbitControls及 Raycaster 等交互控件若源码包含在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点import * as THREE from threeimport { OrbitControls } from three/examples/jsm/controls/OrbitControls.jsvar scene new THREE.Scene(); var camera new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); camera.position.set(3, 5, 8).setLength(15); var renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0x101005); renderer.shadowMap.enabled true; renderer.shadowMap.type THREE.PCFSoftShadowMap; document.body.appendChild(renderer.domElement); window.addEventListener(resize, event { camera.aspect innerWidth / innerHeight; camera.updateProjectionMatrix() renderer.setSize(innerWidth, innerHeight); })var controls new OrbitControls(camera, renderer.domElement); controls.enablePan false; controls.minPolarAngle THREE.MathUtils.degToRad(60); controls.maxPolarAngle THREE.MathUtils.degToRad(95); controls.minDistance 4; controls.maxDistance 20; controls.autoRotate true; controls.autoRotateSpeed 1; controls.target.set(0, 2, 0); controls.update();var light new THREE.DirectionalLight(0xffffff, 0.025); light.position.setScalar(10); scene.add(light); scene.add(new THREE.AmbientLight(0xffffff, 0.0625));var casePath new THREE.Path(); casePath.moveTo(0, 0); casePath.lineTo(0, 0); casePath.absarc(1.5, 0.5, 0.5, Math.PI1.5, Math.PI2); casePath.lineTo(2, 1.5); casePath.lineTo(1.99, 1.5); casePath.lineTo(1.9, 0.5); var caseGeo new THREE.LatheGeometry(casePath.getPoints(), 64); var caseMat new THREE.MeshStandardMaterial({ color: silver }); var caseMesh new THREE.Mesh(caseGeo, caseMat); caseMesh.castShadow true;var paraffinPath new THREE.Path(); paraffinPath.moveTo(0, -.25); paraffinPath.lineTo(0, -.25); paraffinPath.absarc(1, 0, 0.25, Math.PI1.5, Math.PI2); paraffinPath.lineTo(1.25, 0); paraffinPath.absarc(1.89, 0.1, 0.1, Math.PI1.5, Math.PI2); var paraffinGeo new THREE.LatheGeometry(paraffinPath.getPoints(), 64); paraffinGeo.translate(0, 1.25, 0); var paraffinMat new THREE.MeshStandardMaterial({ color: 0xffff99, side: THREE.BackSide, metalness: 0, roughness: 0.75 }); var paraffinMesh new THREE.Mesh(paraffinGeo, paraffinMat); caseMesh.add(paraffinMesh);var candlewickProfile new THREE.Shape(); candlewickProfile.absarc(0, 0, 0.0625, 0, Math.PI * 2);var candlewickCurve new THREE.CatmullRomCurve3([ new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0.5, -0.0625), new THREE.Vector3(0.25, 0.5, 0.125) ]);var candlewickGeo new THREE.ExtrudeGeometry(candlewickProfile, { steps: 8, bevelEnabled: false, extrudePath: candlewickCurve });var colors []; var color1 new THREE.Color(black); var color2 new THREE.Color(0x994411); var color3 new THREE.Color(0xffff44); for (let i 0; i candlewickGeo.attributes.position.count; i) { if (candlewickGeo.attributes.position.getY(i) 0.4) { color1.toArray(colors, i * 3); } else { color2.toArray(colors, i * 3); }; if (candlewickGeo.attributes.position.getY(i) 0.15) color3.toArray(colors, i * 3); } candlewickGeo.setAttribute(color, new THREE.BufferAttribute(new Float32Array(colors), 3)); candlewickGeo.translate(0, 0.95, 0); var candlewickMat new THREE.MeshBasicMaterial({ vertexColors: true });var candlewickMesh new THREE.Mesh(candlewickGeo, candlewickMat); caseMesh.add(candlewickMesh);// candle light var candleLight new THREE.PointLight(0xffaa33, 1, 5, 2); candleLight.position.set(0, 3, 0); candleLight.castShadow true; caseMesh.add(candleLight); var candleLight2 new THREE.PointLight(0xffaa33, 1, 10, 2); candleLight2.position.set(0, 4, 0); candleLight2.castShadow true; caseMesh.add(candleLight2);var flameMaterials []; function flame(isFrontSide) { let flameGeo new THREE.SphereGeometry(0.5, 32, 32); flameGeo.translate(0, 0.5, 0); let flameMat getFlameMaterial(true); flameMaterials.push(flameMat); let flame new THREE.Mesh(flameGeo, flameMat); flame.position.set(0.06, 1.2, 0.06); flame.rotation.y THREE.MathUtils.degToRad(-45); caseMesh.add(flame); }flame(false); flame(true);// table var tableGeo new THREE.CylinderGeometry(14, 14, 0.5, 64); tableGeo.translate(0, -0.25, 0); var tableMat new THREE.MeshStandardMaterial({ map: new THREE.TextureLoader().load(https://threejs.org/examples/textures/hardwood2_diffuse.jpg), metalness: 0, roughness: 0.75 }); var tableMesh new THREE.Mesh(tableGeo, tableMat); tableMesh.receiveShadow true;tableMesh.add(caseMesh); scene.add(tableMesh);var clock new THREE.Clock(); var time 0;render(); function render() { requestAnimationFrame(render); time clock.getDelta(); flameMaterials[0].uniforms.time.value time; flameMaterials[1].uniforms.time.value time; candleLight2.position.x Math.sin(timeMath.PI)0.25; candleLight2.position.z Math.cos(timeMath.PI0.75) * 0.25; candleLight2.intensity 2 Math.sin(timeMath.PI2)Math.cos(timeMath.PI1.5)0.25; controls.update(); renderer.render(scene, camera); }//reference Paul https://codepen.io/prisoner849/pen/XPVGLp function getFlameMaterial(isFrontSide) { let side isFrontSide ? THREE.FrontSide : THREE.BackSide; return new THREE.ShaderMaterial({ uniforms: { time: { value: 0 } }, vertexShader:uniform float time; varying vec2 vUv; varying float hValue; float random (in vec2 st) { return fract(sin(dot(st.xy, vec2(12.9898,78.233)))43758.5453123);}float noise (in vec2 st) { vec2 i floor(st); vec2 f fract(st);// Four corners in 2D of a tile float a random(i); float b random(i vec2(1.0, 0.0)); float c random(i vec2(0.0, 1.0)); float d random(i vec2(1.0, 1.0));// Smooth Interpolation// Cubic Hermine Curve. Same as SmoothStep() vec2 u ff(3.0-2.0*f); // u smoothstep(0.,1.,f);// Mix 4 coorners percentages return mix(a, b, u.x) (c - a)u.y(1.0 - u.x) (d - b)u.xu.y; }void main() { vUv uv; vec3 pos position;pos * vec3(0.8, 2, 0.725); hValue position.y; //float sinT sin(time2.)0.5 0.5; float posXZlen length(position.xz);pos.y 1. (cos((posXZlen 0.25)3.1415926)0.25 noise(vec2(0, time))0.125 noise(vec2(position.x time, position.z time))0.5)position.y; // flame heightpos.x noise(vec2(time2., (position.y - time)4.0))hValue0.0312; // flame trembling pos.z noise(vec2((position.y - time)4.0, time2.))hValue0.0312; // flame tremblinggl_Position projectionMatrixmodelViewMatrixvec4(pos,1.0); }, fragmentShader:varying float hValue; varying vec2 vUv;// honestly stolen from https://www.shadertoy.com/view/4dsSzr vec3 heatmapGradient(float t) { return clamp((pow(t, 1.5)0.8 0.2)vec3(smoothstep(0.0, 0.35, t) t0.5, smoothstep(0.5, 1.0, t), max(1.0 - t1.7, t * 7.0 - 6.0)), 0.0, 1.0); }void main() { float v abs(smoothstep(0.0, 0.4, hValue) - 1.); float alpha (1. - v) * 0.99; // bottom transparency alpha - 1. - smoothstep(1.0, 0.97, hValue); // tip transparency gl_FragColor vec4(heatmapGradient(smoothstep(0.0, 0.3, hValue)) * vec3(0.95,0.95,0.4), alpha) ; gl_FragColor.rgb mix(vec3(0,0,1), gl_FragColor.rgb, smoothstep(0.0, 0.3, hValue)); // blueish for bottom gl_FragColor.rgb vec3(1, 0.9, 0.5) * (1.25 - vUv.y); // make the midst brighter gl_FragColor.rgb mix(gl_FragColor.rgb, vec3(0.66, 0.32, 0.03), smoothstep(0.95, 1., hValue)); // tip }, transparent: true, side: side })}完整源码GitHub小结本文提供蜡烛完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库

相关新闻

Mappedbus实战教程:3步实现Java进程间高效通信(含完整代码示例)

Mappedbus实战教程:3步实现Java进程间高效通信(含完整代码示例)

Mappedbus实战教程:3步实现Java进程间高效通信(含完整代码示例) 【免费下载链接】Mappedbus Mappedbus is a low latency message bus for Java microservices utilizing shared memory. http://mappedbus.io 项目地址: https://gitcode.co…

2026/9/23 23:44:35 阅读更多 →
从0到1部署gh_mirrors/back/backend:Docker容器化与CI/CD流程详解

从0到1部署gh_mirrors/back/backend:Docker容器化与CI/CD流程详解

从0到1部署gh_mirrors/back/backend:Docker容器化与CI/CD流程详解 【免费下载链接】backend A template repository for TypeScript backend server 项目地址: https://gitcode.com/gh_mirrors/back/backend GitHub 加速计划(gh_mirrors/back/bac…

2026/10/2 15:13:05 阅读更多 →
PlumeImpactor多语言支持:如何切换界面语言及贡献翻译

PlumeImpactor多语言支持:如何切换界面语言及贡献翻译

PlumeImpactor多语言支持:如何切换界面语言及贡献翻译 【免费下载链接】Impactor Feature rich iOS/tvOS sideloading application written in Rust. 项目地址: https://gitcode.com/gh_mirrors/pl/Impactor PlumeImpactor是一款功能丰富的iOS/tvOS侧载应用&…

2026/10/2 1:13:32 阅读更多 →

最新新闻

Windows 上配置 Codex 全攻略:Node.js、npm 与 VSCode 集成

Windows 上配置 Codex 全攻略:Node.js、npm 与 VSCode 集成

1. 为什么要在 Windows 上折腾 Codex如果你最近在关注 AI 辅助编程这块,大概率已经听说过 Codex 这个名字。它本质上是一个跑在终端里的智能编程助手,能理解你当前项目的上下文,帮你补全代码、解释逻辑、重构函数,甚至直接根据自然…

2026/10/4 8:11:35 阅读更多 →
VASP自旋轨道耦合计算全指南:参数设置、三步流程与排错经验

VASP自旋轨道耦合计算全指南:参数设置、三步流程与排错经验

做第一性原理计算的同行,估计都遇到过这种场景:文献里一个重元素材料的能带,费米面附近忽然劈开几条带,或者一个本应显示金属行为的体系,算完却出现了一个微小的带隙。问作者怎么处理的,对方轻描淡写地回一…

2026/10/4 8:11:35 阅读更多 →
Matlab2018a启动黑屏报错No sandbox解决方案

Matlab2018a启动黑屏报错No sandbox解决方案

1. 这个报错不是Matlab本身的问题,而是Qt框架在Windows上的沙箱机制失效了你刚点开Matlab2018a,还没输入一行代码,界面刚弹出来就“啪”一下黑屏退出,控制台里只留下一行冷冰冰的提示:No sandbox or build area path。…

2026/10/4 8:11:35 阅读更多 →
Codex半年实战盘点:安装配置避坑与省Token技巧

Codex半年实战盘点:安装配置避坑与省Token技巧

过去这半年,Codex 的变化比我预想的大很多。它从一个在终端里敲命令的编程代理,慢慢长成了带桌面客户端、支持自定义模型、能接 MCP 工具的完整开发助手。但更新越频繁,社区里的报错就越热闹:sign-in could not be completed、tok…

2026/10/4 8:11:35 阅读更多 →
从pcap抓包提取GB28181国标PS流:RTP分片重组与验证实战

从pcap抓包提取GB28181国标PS流:RTP分片重组与验证实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 8:11:35 阅读更多 →
插件系统原理与加载失败排查:从IAR到Web Boot实战解析

插件系统原理与加载失败排查:从IAR到Web Boot实战解析

在社区和群里泡久了会发现一个很有意思的现象:凡是带 "plugins" 这个词的问题,十有八九不是问"插件怎么用",就是问"插件为什么起不来"。这次搜过来的热词也很典型——有人搜 IAR plugins 是干什么的&#xff0…

2026/10/4 8:10:35 阅读更多 →

日新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 1:00:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/2 10:36:31 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:35 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/3 9:42:36 阅读更多 →