Three.js 灯罩着色器 | 三维可视化 / AI 提示词

💡 写在前面

最近一直在研究 Three.js 的着色器效果,偶然看到这个灯罩的例子,一下子就被吸引了——那种朦胧的紫色渐变,配上深蓝背景,像极了深夜书房里一盏温暖的台灯。忍不住动手实现了一遍,过程中踩了不少坑,也有一些有趣的发现,想和大家分享一下。

📋 AI 提示词

prompt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用 Three.js 实现【灯罩着色器】,具体要求:

【核心特效】
- 灯罩采用 CylinderGeometry 几何体,创建锥形中空结构
- 使用 ShaderMaterial 自定义着色器,通过 vUv.y 控制透明度渐变
- 灯罩颜色固定为紫色 (#CB00E3),内部包含发光灯芯

【场景与光照】
- 背景色:深蓝色 (#070630) 营造夜晚氛围
- 光源:每个灯罩内部包含 PointLight,power=1000 模拟真实台灯

【交互与控制】
- OrbitControls 鼠标交互,可旋转视角观察灯罩效果

【技术要求】
- Three.js 版本:latest
- 特殊材质:ShaderMaterial 自定义着色器
- 类结构:DeskLamp 灯罩类,包含私有方法创建灯芯和灯罩
- 几何体:SphereGeometry(灯芯)、CylinderGeometry(灯罩)、PlaneGeometry(地面)

🖼️ 效果预览

灯罩着色器

看到这个效果的时候,我脑海里浮现的是:夜深了,书房里三盏台灯静静散发着柔和的光,在深蓝的夜色中格外温暖。这种氛围,正是我想要表达的。

🎮 案例演示

立即体验

建议你拖动鼠标旋转视角,从不同角度看看灯罩的透明渐变效果,特别有感觉!

📖 效果拆解

层次 视觉效果 技术实现
基础 场景构建和相机设置 PerspectiveCamera(45, size.width / size.height, 0.1, 1000),camera.position.set(30, 30, 30)
核心特效 灯罩几何体和透明渐变 CylinderGeometry(1, 5, 3, 32) + ShaderMaterial,vUv.y 控制 alpha
核心特效 灯芯发光效果 SphereGeometry(1, 32, 16) + MeshBasicMaterial + PointLight(power=1000)
增强细节 多个灯罩组合 创建 3 个 DeskLamp 实例,分布在不同位置
增强细节 地面反射 PlaneGeometry(100, 100) + MeshStandardMaterial

🔧 核心技术点

1. DeskLamp 类封装

一开始我直接写了一堆全局变量,后来觉得太乱了,就试着用类来封装。用 ES6 私有方法真的很舒服,把灯芯、灯罩、光源的创建都藏在类里面,外部只需要 new DeskLamp() 就能得到一个完整的台灯对象。

javascript
1
2
3
4
5
6
7
8
class DeskLamp extends Group {
    constructor() {
        super()
        this.#createWick()      // 创建灯芯
        this.#createLampshade() // 创建灯罩
    }
    // 私有方法 #createWick, #createLampshade, #createLight
}

2. CylinderGeometry 锥形灯罩

这里有个小技巧:创建圆锥体其实就是用 CylinderGeometry,把顶部半径设小一点就行。我试了好几个参数组合,最终觉得 topRadius=1、bottomRadius=5 这个比例看起来最自然。

javascript
1
2
3
4
5
6
7
8
9
const cylinderGeometry = new CylinderGeometry(1, 5, 3, 32)
// 参数:topRadius=1, bottomRadius=5, height=3, radialSegments=32
const cylinderMaterial = new ShaderMaterial({
    transparent: true,  // 必须开启透明,一开始忘了加,调试了半天
    uniforms: {
        color: { value: new Color('#CB00E3') }
    },
    // ...
})

3. 透明度渐变着色器 (核心)

这是整个效果的灵魂!我特别喜欢这个渐变效果——从底部的实体紫色,慢慢过渡到顶部的完全透明,像一层薄纱笼罩着灯光。

glsl
1
2
3
4
5
6
uniform vec3 color;      // 灯罩颜色 #CB00E3
varying vec2 vUv;        // UV 坐标,vUv.y 从底部 0 到顶部 1

void main() {
    // alpha = vUv.y:底部完全不透,顶部完全透明
    gl_FragColor = vec4(color, vUv.y);
}

效果原理图:

plaintext
1
2
3
4
      ╭───╮  ← 顶部 vUv.y=1.0, alpha=1.0 完全透明
     / ╲ ╱ \
    /   ╲   \   ← 中间 vUv.y=0.5, alpha=0.5 半透明
   /─────╲─────\ ← 底部 vUv.y=0, alpha=0 不透明

4. 灯芯点光源

把光源放在灯芯里面是个很巧妙的设计!一开始我把光源放在外面,结果光照效果很奇怪,后来想到把它作为灯芯的子元素,跟着灯芯一起移动,效果立刻就对了。

javascript
1
2
3
4
5
#createLight(mesh) {
    const pointLight = new PointLight(0xffffff, 1, 100)
    pointLight.power = 1000  // 这个值调了好多次才满意
    mesh.add(pointLight)    // 将光源添加为灯芯的子元素
}

5. 场景组合

放三个灯罩是为了营造一种温馨的氛围。我试过放一个、两个,总觉得不够热闹,放三个正好,有种对称美。

javascript
1
2
3
4
5
6
7
8
9
const light1 = new DeskLamp()
light1.position.set(0, 10, 0)
scene.add(light1)

const light2 = new DeskLamp()
light2.position.set(10, 10, 0)
scene.add(light2)

const light3 = new DeskLamp()
light3.position.set(0, 10, 10)
scene.add(light3)

💡 调试与优化(踩坑记录)

问题类型 表现形式 解决方案
灯罩不透明 模型完全没有透明效果 ⚠️ 我踩过的坑!忘了设置 transparent: true,ShaderMaterial 默认不透明
透明渐变方向错误 从上到下渐变而非从下到上 检查 vUv.y 的使用方式,可能需要改为 1.0 - vUv.y
光照不均匀 某些角度灯罩过暗 PointLight 已在灯芯内部,注意调整 camera.position 和灯罩位置
性能问题 多个灯罩渲染卡顿 减少 radialSegments(如 32→16),或使用 InstancedMesh

🚀 扩展思路(我的一些脑洞)

变体效果 核心改动 难度 我的想法
可调颜色灯罩 将固定颜色改为可动态修改的 uniform 可以做个颜色选择器,让用户自定义灯罩颜色
渐变方向反转 vUv.y 改为 1.0 - vUv.y 试试看反过来的效果,可能更像真实灯罩?
动态呼吸效果 在 animate 中让 color 随时间变化 ⭐⭐ 模拟呼吸灯效果,增加生命力
灯罩开合动画 修改 CylinderGeometry 参数实现灯罩开合 ⭐⭐ 想象一下台灯打开时灯罩缓缓张开的样子
触控变色 添加 raycaster 监听,实现点击切换颜色 ⭐⭐ 交互性更强,用户体验更好

✨ 我的感悟

写这个例子的时候,我一直在想:技术不仅仅是冰冷的代码,更是表达情感的工具。这个灯罩效果之所以让我喜欢,就是因为它传递出一种温暖、安静的氛围。希望你在实现的过程中,也能找到属于自己的那份感动。


本文档由 ThreeLab 编辑整理,如需转载,请注明出处。