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/7/25 20:36:52 阅读更多 →
从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/7/25 20:36:52 阅读更多 →
PlumeImpactor多语言支持:如何切换界面语言及贡献翻译

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

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

2026/7/25 20:36:52 阅读更多 →

最新新闻

Gemini 3.5长文本处理踩坑:上下文截断问题解决方案

Gemini 3.5长文本处理踩坑:上下文截断问题解决方案

长文本是所有模型的短板,但 Gemini 3.5 踩的坑不一样 过去大半年我一直在研究多模型集成方案,从自研搭建到开源 UI 部署,再到第三方平台,踩了不少坑。最近在titiai.cn上找到了一个比较省心的方案,顺手用 Gemini 3.5 做…

2026/7/25 20:48:56 阅读更多 →
海投、精准投递和关系渠道怎么配合?|蒸汽求职分享

海投、精准投递和关系渠道怎么配合?|蒸汽求职分享

**摘要:**海投、精准投递和关系渠道解决的问题并不相同。留学生需要用一定数量的匹配申请了解市场,用针对性材料争取重点岗位,再通过职业交流理解团队需求、获得反馈和发现机会,并根据实际转化数据持续调整投入。 留学生求职时&am…

2026/7/25 20:48:56 阅读更多 →
SSA-LSTM优化时间序列预测:原理与工程实践

SSA-LSTM优化时间序列预测:原理与工程实践

1. 项目背景与核心价值在时间序列预测领域,LSTM(长短期记忆网络)因其优秀的记忆能力被广泛应用于金融、气象、工业控制等场景。但传统LSTM存在超参数选择困难、收敛速度慢等问题,而麻雀搜索算法(Sparrow Search Algori…

2026/7/25 20:48:56 阅读更多 →
ScaleDown扩展开发指南:构建自定义优化器与压缩器插件

ScaleDown扩展开发指南:构建自定义优化器与压缩器插件

ScaleDown扩展开发指南:构建自定义优化器与压缩器插件 【免费下载链接】scaledown 项目地址: https://gitcode.com/gh_mirrors/sca/scaledown ScaleDown是一款强大的上下文优化与压缩工具,通过构建自定义优化器与压缩器插件,开发者可…

2026/7/25 20:48:56 阅读更多 →
【JAVA毕设源码分享】基于springboot的宠物之家管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

【JAVA毕设源码分享】基于springboot的宠物之家管理系统的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/25 20:48:56 阅读更多 →
第五人格时间计算技巧:从电机进度到技能冷却的完整分析

第五人格时间计算技巧:从电机进度到技能冷却的完整分析

第五人格比赛时间计算技巧:从电机进度到技能冷却的完整分析在第五人格的高端对局和比赛中,精确的时间计算往往是决定胜负的关键因素。很多玩家在实际对战中容易忽略时间细节,导致决策失误。本文将系统讲解第五人格中各种时间要素的计算方法&a…

2026/7/25 20:47:55 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/25 5:08:22 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/25 5:13:53 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻