
Three.js 伪灯光效果 | 三维可视化 / AI 提示词
� 写在前面
最近在研究如何在 Three.js 中实现更逼真的发光效果,传统的 PointLight 虽然好用,但有时候想要更自由的光晕效果。偶然发现了"伪灯光"这个思路——用 Canvas 生成渐变纹理,配合多层 Plane 和 AdditiveBlending,能做出非常漂亮的发光效果。今天就来分享这个有趣的实现。
�📋 AI 提示词
prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用 Three.js 实现【伪灯光效果】,具体要求:
【核心特效】
- 使用 Canvas 动态生成径向渐变纹理作为光晕
- 多层 Plane 叠加,每层透明度递减,形成层次感
- 使用 AdditiveBlending 实现发光叠加效果
- 伪灯光始终面向相机(billboard 效果)
- 添加脉冲动画,让灯光有呼吸感
【场景与光照】
- 深蓝色背景 (#0a0a1a) 营造夜晚氛围
- 地面使用 MeshStandardMaterial 接收环境光
- 场景中随机分布立方体和球体作为点缀
【交互与控制】
- OrbitControls 鼠标交互,可旋转视角
- 灯光自动面向相机,保持正确朝向
【技术要求】
- Three.js 版本:latest
- 自定义 ShaderMaterial 实现发光效果
- CanvasTexture 动态生成纹理
- 使用 blending: THREE.AdditiveBlending
🖼️ 效果预览
看到这个效果的时候,我脑海里浮现的是夜晚的城市霓虹——五彩斑斓的灯光在深色背景下格外耀眼,那种柔和的光晕扩散效果,比传统光源更有氛围感。
🎮 案例演示
建议你拖动鼠标旋转视角,注意观察灯光始终面向你,这种 billboard 效果让光晕看起来更加立体。
📖 效果拆解
| 层次 | 视觉效果 | 技术实现 |
|---|---|---|
| 基础 | 场景构建和相机设置 | PerspectiveCamera(60, ratio, 0.1, 1000),camera.position.set(0, 15, 30) |
| 核心特效 | Canvas 径向渐变纹理 | createGlowTexture() 创建 256x256 渐变图 |
| 核心特效 | 多层光晕叠加 | 3 层 Plane,每层尺寸递增、透明度递减 |
| 核心特效 | 发光叠加 | AdditiveBlending + depthWrite: false |
| 动画 | 脉冲呼吸效果 | sin(uTime) 控制透明度变化 |
| 增强细节 | 场景点缀 | 随机分布的立方体和球体 |
🔧 核心技术点
1. Canvas 动态生成光晕纹理
这是整个效果的基石!用 Canvas 创建径向渐变,从中心的实色过渡到边缘的透明。
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createGlowTexture(color) {
const canvas = document.createElement('canvas')
canvas.width = 256
canvas.height = 256
const ctx = canvas.getContext('2d')
const gradient = ctx.createRadialGradient(128, 128, 0, 128, 128, 128)
gradient.addColorStop(0, color) // 中心完全不透明
gradient.addColorStop(0.3, color.replace('1)', '0.6)')) // 30%处 60% 透明
gradient.addColorStop(0.6, color.replace('1)', '0.2)')) // 60%处 80% 透明
gradient.addColorStop(1, color.replace('1)', '0)')) // 边缘完全透明
ctx.fillStyle = gradient
ctx.fillRect(0, 0, 256, 256)
const texture = new THREE.CanvasTexture(canvas)
texture.needsUpdate = true
return texture
}
渐变效果示意:
plaintext
1
2
3
4
⬤ 中心 (0%): 完全实色
○ 30%: 60% 透明度
○ 60%: 80% 透明度
○ 边缘 (100%): 完全透明
2. 自定义着色器实现发光
着色器负责读取纹理并添加脉冲动画效果:
glsl
1
2
3
4
5
6
7
8
9
uniform sampler2D uTexture;
uniform float uOpacity;
uniform float uTime;
varying vec2 vUv;
void main() {
vec4 texColor = texture2D(uTexture, vUv);
float pulse = 0.8 + 0.2 * sin(uTime * 2.0); // 脉冲动画
gl_FragColor = vec4(texColor.rgb, texColor.a * uOpacity * pulse);
}
关键配置:
javascript
1
2
3
4
5
6
const coreMaterial = new THREE.ShaderMaterial({
transparent: true,
blending: THREE.AdditiveBlending, // 发光叠加
depthWrite: false, // 不写入深度缓冲
side: THREE.DoubleSide
})
3. 多层 Plane 叠加
用多层叠加创造光晕的层次感,这是我最得意的设计!
javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
for (let i = 1; i <= 3; i++) {
const layerMaterial = coreMaterial.clone()
layerMaterial.uniforms.uOpacity.value = 0.3 / i // 每层透明度递减
const layerGeometry = new THREE.PlaneGeometry(
size * (1 + i * 0.5), // 尺寸递增 50%
size * (1 + i * 0.5)
)
const layer = new THREE.Mesh(layerGeometry, layerMaterial)
layer.position.copy(position)
layer.position.y -= i * 0.1 // 每层稍微下移,增加层次感
layer.lookAt(camera.position)
group.add(layer)
}
4. Billboard 效果
让灯光始终面向相机,这是伪灯光的关键:
javascript
1
2
3
4
// 创建时
core.lookAt(camera.position)
// 动画循环中
fakeLight.core.lookAt(camera.position)
5. 性能优化要点
javascript
1
2
3
4
renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true // 保留绘制缓冲,用于截图等
})
💡 调试与优化(踩坑记录)
| 问题类型 | 表现形式 | 解决方案 |
|---|---|---|
| 光晕不发光 | 灯光看起来是平的,没有发光效果 | ⚠️ 必须设置 blending: THREE.AdditiveBlending |
| 光晕被遮挡 | 后面的光晕被前面的物体挡住 | 设置 depthWrite: false 允许穿透 |
| 闪烁问题 | 多层叠加时出现闪烁 | 调整每层的 position.y 稍微错开 |
| 性能问题 | 多个灯光时帧率下降 | 减少层数(从 3 层改为 2 层),或使用 InstancedMesh |
🚀 扩展思路(我的一些脑洞)
| 变体效果 | 核心改动 | 难度 | 我的想法 |
|---|---|---|---|
| 动态颜色变化 | 在 animate 中修改纹理或 uniform | ⭐⭐ | 让灯光随音乐节奏变色 |
| 光晕形状自定义 | 修改 Canvas 绘制逻辑 | ⭐⭐⭐ | 实现星形、心形等特殊光晕 |
| 阴影投射 | 添加自定义阴影纹理 | ⭐⭐⭐ | 让伪灯光也能投射阴影 |
| 交互触发 | 添加 raycaster 检测 | ⭐⭐ | 点击时灯光闪烁或变色 |
✨ 我的感悟
伪灯光这个思路真的很有趣!它让我意识到,有时候不一定要用"正统"的方法,换个角度思考可能会有更好的效果。这种用 2D 纹理模拟 3D 发光的技巧,在性能敏感的场景下特别实用。希望你也能从这个例子中获得一些灵感!
本文档由 ThreeLab 编辑整理,如需转载,请注明出处。







京公网安备 11010502038735号
💬 评论区
评论功能即将上线,敬请期待!