03 光照与阴影
掌握 Three.js 中的各种光源和阴影效果
为什么需要光照
没有光照,你看到的只是黑色的物体轮廓。光让物体有了立体感和色彩。
Three.js 的光照模型基于渲染方程,模拟真实世界中光与物体表面的交互。
光照类型一览
| 类型 | 特点 | 适用场景 |
|---|---|---|
| AmbientLight | 全局均匀照亮 | 补充暗部细节 |
| DirectionalLight | 平行光(太阳) | 室外场景 |
| PointLight | 点光源(灯泡) | 室内照亮 |
| SpotLight | 聚光灯(手电筒) | 舞台、聚光效果 |
| HemisphereLight | 半球光 | 室外环境光 |
| RectAreaLight | 矩形面积光 | 窗光、霓虹灯 |
AmbientLight(环境光)
均匀照亮场景中所有物体,不会产生阴影,也不受位置影响。
js
// color: 光照颜色, intensity: 强度
const ambient = new THREE.AmbientLight(0xffffff, 0.3)
scene.add(ambient)
// 常见值:0.3-0.5
// 值太大会让物体失去立体感(变成平板)1
2
3
4
5
6
2
3
4
5
6
DirectionalLight(平行光)
光线平行发射,像太阳光。可以产生阴影。
js
const dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.position.set(5, 5, 5) // 光照方向 = position - (0,0,0)
scene.add(dirLight)1
2
3
2
3
调整光照方向
js
// 太阳从侧面照射
dirLight.position.set(10, 0, 0) // 光从右向左
// 太阳从正上方
dirLight.position.set(0, 10, 0)
// 太阳在地平线(黄昏效果)
dirLight.position.set(10, 1, 0)1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
PointLight(点光源)
像灯泡,向四周均匀发光。光线从一点向所有方向发射。
js
const pointLight = new THREE.PointLight(0xff6b6b, 1, 100)
pointLight.position.set(0, 5, 0)
scene.add(pointLight)
// 参数:color, intensity, 衰减距离(超过这个距离光照为0)1
2
3
4
5
2
3
4
5
衰减(Attenuation)
真实世界中,光线会随距离衰减:
js
const pointLight = new THREE.PointLight(0xff6b6b, 1, 100, 2)
// 最后一个参数:衰减率(0=不衰减,2=物理正确衰减)1
2
2
SpotLight(聚光灯)
像手电筒,有方向和锥形范围,可以产生锥形阴影。
js
const spotLight = new THREE.SpotLight(0xffffff, 100)
spotLight.position.set(0, 10, 0)
spotLight.target.position.set(0, 0, 0) // 照射目标点
spotLight.angle = Math.PI / 6 // 光锥角度
spotLight.penumbra = 0.3 // 半影(0-1),值越大阴影边缘越柔和
spotLight.decay = 2 // 衰减率
spotLight.distance = 100 // 最大照射距离
scene.add(spotLight)
scene.add(spotLight.target)1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
HemisphereLight(半球光)
模拟室外环境,上方是天空色(蓝),下方是地面色(棕/绿)。
js
const hemiLight = new THREE.HemisphereLight(
0x87ceeb, // 天空色(上方)
0x444444, // 地面色(下方)
0.5 // 强度
)
scene.add(hemiLight)1
2
3
4
5
6
2
3
4
5
6
RectAreaLight(矩形面积光)
从一个矩形平面发射光线,适合模拟窗户透进来的光或霓虹灯。
js
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js'
// 初始化(必须调用)
RectAreaLightUniformsLib.init()
const rectLight = new THREE.RectAreaLight(0xffffff, 5, 10, 5)
rectLight.position.set(0, 5, 0)
rectLight.lookAt(0, 0, 0)
scene.add(rectLight)
// 注意:矩形面积光只支持 MeshStandardMaterial 和 MeshPhysicalMaterial1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
阴影配置
1. 渲染器开启阴影
js
const renderer = new THREE.WebGLRenderer({ antialias: true })
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.shadowMap.enabled = true // 开启阴影
// 阴影类型
renderer.shadowMap.type = THREE.PCFSoftShadowMap // 软阴影(推荐)
// 其他类型:THREE.BasicShadowMap(性能好但硬边)
// THREE.PCFShadowMap(折中)1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 光源开启阴影
js
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.castShadow = true // 开启阴影投射
// 阴影分辨率(越高越清晰,性能开销越大)
directionalLight.shadow.mapSize.width = 2048
directionalLight.shadow.mapSize.height = 2048
// 阴影相机范围(决定了阴影覆盖的区域)
directionalLight.shadow.camera.near = 0.1
directionalLight.shadow.camera.far = 50
directionalLight.shadow.camera.left = -10
directionalLight.shadow.camera.right = 10
directionalLight.shadow.camera.top = 10
directionalLight.shadow.camera.bottom = -10
scene.add(directionalLight)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3. 物体投射和接收阴影
js
// 立方体投射阴影
cube.castShadow = true
// 平面接收阴影(地面、墙面)
plane.receiveShadow = true1
2
3
4
5
2
3
4
5
4. 阴影相机辅助调试
js
const shadowHelper = new THREE.DirectionalLightHelper(directionalLight, 5)
scene.add(shadowHelper)
// 或者显示阴影相机范围
const cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
scene.add(cameraHelper)1
2
3
4
5
6
2
3
4
5
6
阴影优化技巧
覆盖范围合理
阴影相机的范围应该刚好覆盖需要显示阴影的区域:
js
// 避免:范围过大导致阴影像素分散、模糊
directionalLight.shadow.camera.left = -100 // ❌ 太大
directionalLight.shadow.camera.left = -10 // ✅ 刚好
// 根据场景大小调整
const sceneSize = 20
directionalLight.shadow.camera.left = -sceneSize / 2
directionalLight.shadow.camera.right = sceneSize / 2
directionalLight.shadow.camera.top = sceneSize / 2
directionalLight.shadow.camera.bottom = -sceneSize / 21
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
使用阴影贴图偏移避免瑕疵
js
directionalLight.shadow.bias = -0.0001 // 避免阴影摩尔纹(条纹)1
只对需要的物体投射阴影
js
// 大面积背景不需要投射阴影
backgroundMesh.castShadow = false
// 小物件投射阴影增加真实感
character.castShadow = true1
2
3
4
5
2
3
4
5
常见光照组合
室外白天
js
// 太阳(主光源)
const sun = new THREE.DirectionalLight(0xfff5e0, 1.5)
sun.position.set(50, 100, 50)
sun.castShadow = true
// 天空光(补充)
const sky = new THREE.HemisphereLight(0x87ceeb, 0x3d5c35, 0.6)
// 阴影配置
sun.shadow.mapSize.width = 2048
sun.shadow.mapSize.height = 2048
sun.shadow.camera.near = 0.1
sun.shadow.camera.far = 300
sun.shadow.camera.left = -50
sun.shadow.camera.right = 50
sun.shadow.camera.top = 50
sun.shadow.camera.bottom = -50
scene.add(sun)
scene.add(sky)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
室内灯光
js
// 环境光(基础亮度)
scene.add(new THREE.AmbientLight(0x404040, 0.4))
// 主灯(天花板中央)
const mainLight = new THREE.PointLight(0xfff5e0, 1, 20)
mainLight.position.set(0, 4, 0)
mainLight.castShadow = true
// 补光(减少明暗对比)
const fillLight = new THREE.PointLight(0x87ceeb, 0.3, 15)
fillLight.position.set(-3, 3, 3)
scene.add(mainLight)
scene.add(fillLight)1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
舞台聚光
js
// 背景环境
scene.add(new THREE.AmbientLight(0x111111, 0.2))
// 聚光灯(主角)
const spotlight = new THREE.SpotLight(0xffffff, 200)
spotlight.position.set(0, 8, 5)
spotlight.target.position.set(0, 0, 0)
spotlight.angle = Math.PI / 8
spotlight.penumbra = 0.3
spotlight.decay = 2
spotlight.distance = 50
spotlight.castShadow = true
scene.add(spotlight)
scene.add(spotlight.target)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
光照与性能
| 光源类型 | 性能开销 | 阴影开销 |
|---|---|---|
| AmbientLight | 低 | 无 |
| DirectionalLight | 中 | 高 |
| PointLight | 中(×N个光源) | 高 |
| SpotLight | 高 | 最高 |
| HemisphereLight | 低 | 无 |
| RectAreaLight | 高 | 无 |
建议:场景中同时存在的聚光灯不超过 3 个。