光源类型
Three.js 提供了多种光源类型来模拟不同的光照效果。正确选择和配置光源是实现真实感渲染的关键。
概述
光源决定了场景中的光照效果,影响物体的明暗、阴影和整体氛围。Three.js 提供了环境光、点光源、平行光、聚光灯等多种光源类型。
光源类型架构
code
THREE.Light (基类)
├── AmbientLight 环境光 - 均匀照明
├── HemisphereLight 半球光 - 天空/地面环境光
├── DirectionalLight 平行光 - 太阳光
├── PointLight 点光源 - 灯泡
├── SpotLight 聚光灯 - 手电筒/舞台灯
├── RectAreaLight 矩形区域光 - 窗户/屏幕
└── LightProbe 光照探针 - 烘焙光照光源特性对比
| 光源类型 | 方向性 | 阴影 | 衰减 | 性能消耗 | 典型用途 |
|---|---|---|---|---|---|
| AmbientLight | 无 | ❌ | 无 | 极低 | 基础照明 |
| HemisphereLight | 上下 | ❌ | 无 | 低 | 户外环境光 |
| DirectionalLight | 平行 | ✅ | 无 | 中 | 太阳光 |
| PointLight | 全向 | ✅ | 有 | 高 | 灯泡、火光 |
| SpotLight | 锥形 | ✅ | 有 | 高 | 手电筒、舞台灯 |
| RectAreaLight | 矩形 | ❌ | 有 | 高 | 窗户、广告牌 |
光源基类
所有光源都继承自 THREE.Light 基类:
javascript
import * as THREE from 'three';
// 光源基类构造函数
const light = new THREE.Light(color, intensity);
// 基类属性
light.color // 光源颜色(THREE.Color)
light.intensity // 光源强度
light.type // 光源类型字符串
// 方法
light.color.set(0xff0000); // 设置颜色
light.intensity = 2; // 设置强度Light 基类属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
| type | string | - | 光源类型(只读) |
环境光(AmbientLight)
环境光均匀地照亮场景中的所有物体,没有方向,不能产生阴影。
javascript
// 创建环境光
const ambientLight = new THREE.AmbientLight(
0x404040, // 颜色(深灰色)
1 // 强度
);
scene.add(ambientLight);
// 调整强度
ambientLight.intensity = 0.5;
// 设置颜色
ambientLight.color.set(0xffffff);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
特点:
- 无方向性,均匀照亮所有物体
- 不产生阴影
- 性能消耗极低
- 强度不能太高,否则画面发白
适用场景:
javascript
// 夜间场景 - 微弱蓝灰色环境光
const nightAmbient = new THREE.AmbientLight(0x1a1a2e, 0.3);
// 白天场景 - 中等强度白色环境光
const dayAmbient = new THREE.AmbientLight(0xffffff, 0.5);
// 室内场景 - 暖色调环境光
const indoorAmbient = new THREE.AmbientLight(0xffffee, 0.4);
// 地下场景 - 深色调环境光
const undergroundAmbient = new THREE.AmbientLight(0x222233, 0.2);平行光(DirectionalLight)
平行光模拟太阳光,光线是平行的,常用于模拟远距离光源。
javascript
// 创建平行光
const directionalLight = new THREE.DirectionalLight(
0xffffff, // 颜色
1 // 强度
);
// 设置位置(光线方向 = 从位置指向目标点)
directionalLight.position.set(5, 10, 5);
// 设置目标点(默认为原点)
directionalLight.target.position.set(0, 0, 0);
scene.add(directionalLight.target); // 必须将目标添加到场景
scene.add(directionalLight);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
DirectionalLight 特有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Vector3 | (0,1,0) | 光源位置 |
| target | Object3D | - | 光照目标点 |
| castShadow | boolean | false | 是否投射阴影 |
| shadow | DirectionalLightShadow | - | 阴影配置 |
阴影配置:
javascript
// 启用阴影
directionalLight.castShadow = true;
// 阴影贴图尺寸
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
// 阴影相机(正交相机)范围
directionalLight.shadow.camera.near = 0.5;
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;
// 阴影质量参数
directionalLight.shadow.bias = -0.0001; // 阴影偏移
directionalLight.shadow.radius = 1; // 软阴影半径阴影相机调试:
javascript
// 可视化阴影相机范围
const helper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(helper);特点:
- 光线平行,模拟太阳光
- 光照强度不随距离衰减
- 可以产生阴影
- 使用正交相机计算阴影
点光源(PointLight)
点光源从一个点向所有方向发射光线,类似于灯泡。
javascript
// 创建点光源
const pointLight = new THREE.PointLight(
0xffffff, // 颜色
1, // 强度
100, // 距离(影响范围,0 表示无限制)
2 // 衰减系数
);
// 设置位置
pointLight.position.set(0, 5, 0);
scene.add(pointLight);
// 可视化光源位置
const pointLightHelper = new THREE.PointLightHelper(pointLight, 1);
scene.add(pointLightHelper);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
| distance | number | 0 | 最大距离(0=无限) |
| decay | number | 2 | 衰减系数 |
PointLight 特有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Vector3 | (0,0,0) | 光源位置 |
| distance | number | 0 | 光照距离(0=无限) |
| decay | number | 2 | 衰减系数 |
| castShadow | boolean | false | 是否投射阴影 |
| shadow | PointLightShadow | - | 阴影配置 |
衰减计算:
javascript
// 光照强度衰减公式
// 光照 = color * intensity / (decay * distance²)
// 衰减系数说明
pointLight.decay = 0; // 无衰减(不推荐)
pointLight.decay = 1; // 线性衰减
pointLight.decay = 2; // 二次衰减(默认,最接近真实物理)
// 距离限制
pointLight.distance = 100; // 超过 100 单位后光照为 0
pointLight.distance = 0; // 无限距离特点:
- 向所有方向发射
- 强度随距离衰减
- 可以产生阴影(使用立方体贴图,消耗较大)
- 适合局部照明、灯泡、火光
聚光灯(SpotLight)
聚光灯从一点沿锥形方向发射光线,类似于手电筒。
javascript
// 创建聚光灯
const spotLight = new THREE.SpotLight(
0xffffff, // 颜色
1, // 强度
100, // 距离
Math.PI / 6, // 角度(弧度)
0.1, // 半影衰减
2 // 衰减系数
);
// 设置位置和目标
spotLight.position.set(0, 10, 0);
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight);
scene.add(spotLight.target);
// 可视化聚光灯
const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
| distance | number | 0 | 光照距离 |
| angle | number | π/3 | 锥形角度(弧度) |
| penumbra | number | 0 | 半影衰减(0-1) |
| decay | number | 2 | 衰减系数 |
SpotLight 特有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| position | Vector3 | (0,0,0) | 光源位置 |
| target | Object3D | - | 光照目标 |
| angle | number | π/3 | 锥形角度 |
| penumbra | number | 0 | 边缘柔和度 |
| distance | number | 0 | 光照距离 |
| decay | number | 2 | 衰减系数 |
| castShadow | boolean | false | 是否投射阴影 |
聚光灯参数详解:
javascript
// 聚光灯角度(锥形张开角度)
spotLight.angle = Math.PI / 6; // 30 度
spotLight.angle = Math.PI / 4; // 45 度
// 半影衰减(边缘柔和度,0-1)
spotLight.penumbra = 0; // 硬边缘
spotLight.penumbra = 0.5; // 中等柔和
spotLight.penumbra = 1; // 完全柔和
// 聚焦(影响阴影质量)
spotLight.shadow.focus = 1; // 聚焦程度(0-1)特点:
- 锥形光束
- 可调整角度和边缘柔和度
- 可以产生阴影
- 适合舞台灯光、手电筒效果
半球光(HemisphereLight)
半球光模拟天空和地面的环境光,提供自然的户外光照。
javascript
// 创建半球光
const hemisphereLight = new THREE.HemisphereLight(
0xffffbb, // 天空颜色(淡黄色)
0x080820, // 地面颜色(深蓝色)
1 // 强度
);
scene.add(hemisphereLight);
// 调整颜色
hemisphereLight.color.set(0x87CEEB); // 天空蓝
hemisphereLight.groundColor.set(0x8B4513); // 地面棕
// 可视化
const hemisphereLightHelper = new THREE.HemisphereLightHelper(
hemisphereLight,
5 // 辅助球体大小
);
scene.add(hemisphereLightHelper);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| skyColor | Color | 0xffffff | 天空颜色 |
| groundColor | Color | 0xffffff | 地面颜色 |
| intensity | number | 1 | 光源强度 |
HemisphereLight 特有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 天空颜色 |
| groundColor | Color | 0xffffff | 地面颜色 |
| position | Vector3 | (0,100,0) | 位置(只影响方向) |
不同时间段的半球光:
javascript
// 清晨 - 暖色调天空
const morningLight = new THREE.HemisphereLight(
0xffd4a0, // 天空:暖橙色
0x555555, // 地面:灰色
0.5
);
// 中午 - 明亮蓝天
const noonLight = new THREE.HemisphereLight(
0x87CEEB, // 天空:浅蓝色
0x8B4513, // 地面:棕色
0.8
);
// 黄昏 - 橙红色天空
const sunsetLight = new THREE.HemisphereLight(
0xff6b35, // 天空:橙红色
0x4a4a4a, // 地面:深灰色
0.4
);
// 夜晚 - 深蓝天空
const nightLight = new THREE.HemisphereLight(
0x1a1a3e, // 天空:深蓝色
0x0a0a1a, // 地面:深黑色
0.2
);特点:
- 模拟天空和地面反射光
- 提供自然的上下明暗过渡
- 不产生阴影
- 适合户外场景
矩形区域光(RectAreaLight)
矩形区域光从矩形区域发射光线,产生柔和的光照效果。
javascript
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
// 初始化(必须)
RectAreaLightUniformsLib.init();
// 创建矩形区域光
const rectLight = new THREE.RectAreaLight(
0xffffff, // 颜色
5, // 强度
4, // 宽度
2 // 高度
);
// 设置位置和旋转
rectLight.position.set(0, 5, 0);
rectLight.rotation.x = -Math.PI / 2; // 面向下
scene.add(rectLight);
// 可视化
const rectLightHelper = new RectAreaLightHelper(rectLight);
rectLight.add(rectLightHelper);构造参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | number | 1 | 光源强度 |
| width | number | 10 | 矩形宽度 |
| height | number | 10 | 矩形高度 |
RectAreaLight 特有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| width | number | 10 | 矩形宽度 |
| height | number | 10 | 矩形高度 |
| intensity | number | 1 | 光源强度 |
注意事项
- 只支持
MeshStandardMaterial和MeshPhysicalMaterial - 不产生阴影
- 必须调用
RectAreaLightUniformsLib.init() - 光源方向由物体朝向决定(默认朝向 +Z)
特点:
- 从矩形区域发射
- 柔和的光照效果
- 不产生阴影
- 适合模拟窗户、屏幕、广告牌等面光源
光源属性详解
颜色设置
javascript
// 多种方式设置颜色
light.color.set(0xff0000); // 十六进制
light.color.set('red'); // 颜色名称
light.color.set('rgb(255, 0, 0)'); // RGB 字符串
light.color.set('hsl(0, 100%, 50%)'); // HSL 字符串
light.color.setRGB(1, 0, 0); // RGB 分量
light.color.setHSL(0, 1, 0.5); // HSL 分量
// 获取颜色
const hex = light.color.getHex(); // 十六进制
const r = light.color.r; // 红色分量
const g = light.color.g; // 绿色分量
const b = light.color.b; // 蓝色分量强度调整
javascript
// 设置强度
light.intensity = 2;
// 不同场景的推荐强度
AmbientLight: 0.3 - 0.8 // 基础照明
DirectionalLight: 0.5 - 2.0 // 主光源
PointLight: 0.5 - 3.0 // 局部照明
SpotLight: 1.0 - 3.0 // 聚光效果
HemisphereLight: 0.3 - 1.0 // 环境光
RectAreaLight: 1.0 - 10.0 // 面光源位置和目标
javascript
// 设置位置
light.position.set(5, 10, 5);
light.position.x = 5;
light.position.y = 10;
light.position.z = 5;
// 设置目标(平行光、聚光灯)
light.target.position.set(0, 0, 0);
// 将目标添加到场景(重要!)
scene.add(light.target);
// 光线方向 = normalize(target.position - light.position)光源使用示例
基础光照配置
javascript
import * as THREE from 'three';
// 创建场景
const scene = new THREE.Scene();
// 1. 环境光(基础照明)
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
// 2. 平行光(主光源,模拟太阳)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(10, 10, 10);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 3. 点光源(补光)
const pointLight = new THREE.PointLight(0xff6600, 0.5, 50);
pointLight.position.set(-5, 5, -5);
scene.add(pointLight);
// 创建物体
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);
// 地面
const groundGeometry = new THREE.PlaneGeometry(20, 20);
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x888888 });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);夜间场景
javascript
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a1a);
// 微弱环境光
const ambientLight = new THREE.AmbientLight(0x1a1a2e, 0.3);
scene.add(ambientLight);
// 月光(平行光)
const moonLight = new THREE.DirectionalLight(0x4444ff, 0.5);
moonLight.position.set(10, 20, 5);
scene.add(moonLight);
// 路灯(点光源)
const streetLights = [];
for (let i = 0; i < 5; i++) {
const light = new THREE.PointLight(0xffaa00, 1, 20, 2);
light.position.set(i * 10 - 20, 5, 0);
light.castShadow = true;
scene.add(light);
streetLights.push(light);
}室内场景
javascript
const scene = new THREE.Scene();
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
// 主光源(吸顶灯)
const ceilingLight = new THREE.PointLight(0xffffee, 1, 30, 2);
ceilingLight.position.set(0, 4, 0);
ceilingLight.castShadow = true;
scene.add(ceilingLight);
// 窗户光(平行光)
const windowLight = new THREE.DirectionalLight(0x87CEEB, 0.5);
windowLight.position.set(5, 3, 0);
scene.add(windowLight);
// 台灯(聚光灯)
const deskLight = new THREE.SpotLight(0xffaa55, 1, 10, Math.PI / 4, 0.5, 2);
deskLight.position.set(-3, 2, 2);
deskLight.target.position.set(-3, 0, 2);
scene.add(deskLight);
scene.add(deskLight.target);舞台灯光
javascript
const scene = new THREE.Scene();
// 微弱环境光
const ambientLight = new THREE.AmbientLight(0x000000, 0.1);
scene.add(ambientLight);
// 主聚光灯
const mainSpot = new THREE.SpotLight(0xffffff, 2, 50, Math.PI / 6, 0.5, 1);
mainSpot.position.set(0, 15, 10);
mainSpot.target.position.set(0, 0, 0);
mainSpot.castShadow = true;
scene.add(mainSpot);
// 彩色聚光灯
const colors = [0xff0000, 0x00ff00, 0x0000ff];
const spotLights = colors.map((color, i) => {
const spot = new THREE.SpotLight(color, 1, 30, Math.PI / 8, 0.3, 2);
spot.position.set((i - 1) * 10, 10, 5);
spot.target.position.set(0, 0, 0);
scene.add(spot);
return spot;
});
// 动态效果
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
// 旋转彩色灯光
spotLights.forEach((spot, i) => {
spot.target.position.x = Math.sin(time + i * Math.PI * 2 / 3) * 5;
spot.target.position.z = Math.cos(time + i * Math.PI * 2 / 3) * 5;
});
renderer.render(scene, camera);
}
animate();性能优化
光源数量控制
javascript
// ❌ 不推荐:大量光源(严重影响性能)
for (let i = 0; i < 100; i++) {
const light = new THREE.PointLight(0xffffff, 0.1);
light.position.set(Math.random() * 10, Math.random() * 10, Math.random() * 10);
scene.add(light);
}
// ✅ 推荐:少量光源 + 环境贴图
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
const mainLight = new THREE.DirectionalLight(0xffffff, 1);
scene.add(mainLight);
// 使用环境贴图模拟间接光照
scene.environment = envMap;阴影性能优化
javascript
// 根据需求设置阴影贴图尺寸
light.shadow.mapSize.width = 1024; // 移动端:512-1024
light.shadow.mapSize.height = 1024; // 桌面端:1024-2048
// 紧凑的阴影相机范围
light.shadow.camera.near = 1;
light.shadow.camera.far = 20;
light.shadow.camera.left = -5;
light.shadow.camera.right = 5;
// 只在需要时启用阴影
light.castShadow = true;光源数量限制
| 平台 | 推荐光源数 | 说明 |
|---|---|---|
| 移动端 | ≤ 3 | 避免实时阴影 |
| 桌面端 | ≤ 8 | 可适当增加 |
| 高性能设备 | ≤ 16 | 需测试验证 |
常见问题解答
Q: 场景太暗或太亮怎么办?
A: 调整光源强度和环境光:
javascript
// 场景太暗
ambientLight.intensity = 0.5; // 增加环境光
directionalLight.intensity = 1.5; // 增加主光
// 场景太亮
ambientLight.intensity = 0.2; // 降低环境光
directionalLight.intensity = 0.5; // 降低主光Q: 物体看起来很平,没有立体感?
A: 缺少方向光或光源位置不合适:
javascript
// 添加方向光创建明暗对比
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5); // 斜上方
scene.add(directionalLight);
// 降低环境光强度
ambientLight.intensity = 0.3;Q: 如何实现真实的光照效果?
A: 使用标准材质和环境贴图:
javascript
// 使用 MeshStandardMaterial 或 MeshPhysicalMaterial
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.5,
roughness: 0.5
});
// 添加环境贴图
scene.environment = envMap;Q: 点光源和聚光灯的阴影为什么很慢?
A: 这两种光源使用立方体贴图渲染阴影,消耗较大:
javascript
// 降低阴影贴图分辨率
pointLight.shadow.mapSize.width = 512;
pointLight.shadow.mapSize.height = 512;
// 或者只在必要时启用阴影
pointLight.castShadow = false; // 默认关闭Q: 如何模拟白天/夜晚的时间变化?
A: 动态调整光源参数:
javascript
function updateLighting(timeOfDay) {
// timeOfDay: 0-1,0=午夜,0.5=正午
// 太阳位置
const angle = timeOfDay * Math.PI;
sunLight.position.set(
Math.cos(angle) * 50,
Math.sin(angle) * 50,
0
);
// 太阳强度
sunLight.intensity = Math.max(0, Math.sin(angle));
// 天空颜色
const skyColor = new THREE.Color().lerpColors(
new THREE.Color(0x1a1a2e), // 夜晚
new THREE.Color(0x87CEEB), // 白天
Math.sin(angle)
);
hemisphereLight.color.copy(skyColor);
}最佳实践
- 合理搭配光源:环境光 + 主光源 + 补光(三点光照)
- 控制光源数量:移动端不超过 3 个,桌面端不超过 8 个
- 优化阴影:合理设置阴影贴图尺寸和相机范围
- 使用辅助器:开发时使用光源辅助器可视化
- 考虑性能:实时阴影消耗较大,静态场景可使用光照贴图
- 材质选择:使用 MeshStandardMaterial 以获得正确的光照响应