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 编辑整理,如需转载,请注明出处。