1. 五种光源特性
Three.js 的光源系统模仿真实世界的光照行为。不同光源有不同的物理模型和性能开销:
| 光源 | 光照形状 | 支持阴影 | 性能 | 典型用途 |
|---|---|---|---|---|
AmbientLight | 无方向(全局均匀) | 否 | 极高 | 基础环境光,防止暗面全黑 |
DirectionalLight | 平行光(模拟太阳) | 是 | 高 | 室外场景主光源 |
PointLight | 从点向四周发散 | 是(昂贵) | 中 | 灯泡、火焰、游戏道具 |
SpotLight | 圆锥形聚光 | 是 | 中低 | 台灯、舞台追光、产品展示 |
HemisphereLight | 天空/地面双色 | 否 | 高 | 室外天光模拟(配合方向光) |
// AmbientLight — 基础环境光,无方向无阴影
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);
// DirectionalLight — 平行光(模拟太阳)
const dirLight = new THREE.DirectionalLight(0xfff5e0, 2); // 暖白光
dirLight.position.set(5, 10, 5);
scene.add(dirLight);
scene.add(dirLight.target); // 目标点(默认 origin)
// PointLight — 点光源
const pointLight = new THREE.PointLight(
0xff8833, // 颜色
5, // 强度
10, // 衰减距离(0=不衰减)
2 // 衰减指数(2=物理正确)
);
pointLight.position.set(2, 3, 0);
scene.add(pointLight);
// SpotLight — 聚光灯
const spotLight = new THREE.SpotLight(
0xffffff, // 颜色
10, // 强度
20, // 最大距离
Math.PI / 6, // 半锥角(30°)
0.1, // 边缘柔化(0=硬边,1=很软)
1 // 衰减
);
spotLight.position.set(0, 5, 0);
scene.add(spotLight);
scene.add(spotLight.target);
// HemisphereLight — 半球光(天空色+地面色)
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x4a3728, 0.6);
scene.add(hemi);
2. 阴影:castShadow / receiveShadow / shadowMap
实时阴影需要在三个地方分别开启:渲染器、光源、需要投射/接收阴影的物体。
// ── 步骤 1:开启渲染器阴影 ──
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 软阴影(推荐)
// 其他选项:
// THREE.BasicShadowMap — 硬阴影,最快
// THREE.PCFShadowMap — 多采样软化
// THREE.PCFSoftShadowMap — 更软,性能适中
// THREE.VSMShadowMap — 方差阴影,支持模糊
// ── 步骤 2:光源开启投影 ──
// DirectionalLight 和 SpotLight 支持阴影
dirLight.castShadow = true;
// 配置阴影贴图分辨率(越大越清晰,性能越低)
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
// 调整阴影相机的视锥体(DirectionalLight)
// 尽量缩小 near/far 和 left/right 范围以提高阴影精度
dirLight.shadow.camera.near = 0.1;
dirLight.shadow.camera.far = 50;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
// SpotLight 阴影相机配置
spotLight.shadow.camera.near = 0.5;
spotLight.shadow.camera.far = 30;
spotLight.shadow.camera.fov = 40;
// ── 步骤 3:物体设置投影/接收阴影 ──
cube.castShadow = true; // 投射阴影(产生影子)
cube.receiveShadow = true; // 接收阴影(显示他人的影子)
floor.castShadow = false;
floor.receiveShadow = true; // 地面只接收,不投射
// 调试:可视化阴影相机范围
const shadowHelper = new THREE.CameraHelper(dirLight.shadow.camera);
scene.add(shadowHelper);
Shadow Acne(阴影痤疮):物体在自身投影的阴影中出现条纹。解决方案:调整 shadow.bias(通常设为 -0.001 到 -0.0001)。
3. 光照性能优化
- 减少能投射阴影的光源数量:阴影渲染代价很高,每个投射阴影的光源都需要额外的渲染 Pass。场景中最多使用 1-2 个带阴影的光源。
- 缩小阴影贴图分辨率:512 或 1024 对很多场景足够,2048 用于需要高质量阴影的主光源。
- 使用 HemisphereLight 代替 AmbientLight + DirectionalLight 组合:天空色模拟更真实,性能更好。
- 烘焙光照(LightMap):静态场景的光照预先计算并保存为纹理,运行时零开销——游戏场景常用此技术。
// 光照辅助器——可视化光源位置和范围(开发时使用)
const dirHelper = new THREE.DirectionalLightHelper(dirLight, 2);
scene.add(dirHelper);
const pointHelper = new THREE.PointLightHelper(pointLight, 0.3);
scene.add(pointHelper);
const spotHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotHelper);
// 动态更新辅助器(光源移动时必须调用)
function animate() {
requestAnimationFrame(animate);
spotHelper.update(); // SpotLight 需要每帧更新辅助器
renderer.render(scene, camera);
}
4. 实战:3D 产品展示台(PBR + 多光源 + 阴影)
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111827);
const camera = new THREE.PerspectiveCamera(50, w/h, 0.1, 100);
camera.position.set(0, 3, 6);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping; // 电影色调映射
renderer.toneMappingExposure = 1.2;
// ── 展台 ──
const pedestalGeo = new THREE.CylinderGeometry(1.5, 2, 0.3, 32);
const pedestalMat = new THREE.MeshStandardMaterial({
color: 0x1a1a2e, roughness: 0.2, metalness: 0.8
});
const pedestal = new THREE.Mesh(pedestalGeo, pedestalMat);
pedestal.receiveShadow = true;
scene.add(pedestal);
// ── 产品(蓝色金属球)──
const product = new THREE.Mesh(
new THREE.SphereGeometry(0.8, 64, 32),
new THREE.MeshStandardMaterial({
color: 0x049EF4, roughness: 0.1, metalness: 0.9
})
);
product.position.y = 1.1;
product.castShadow = true;
product.receiveShadow = true;
scene.add(product);
// ── 地面 ──
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ color: 0x0a0f1e, roughness: 0.8 })
);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -0.15;
floor.receiveShadow = true;
scene.add(floor);
// ── 主光源(方向光)──
const keyLight = new THREE.DirectionalLight(0xfff8e7, 3);
keyLight.position.set(4, 8, 4);
keyLight.castShadow = true;
keyLight.shadow.mapSize.set(2048, 2048);
keyLight.shadow.camera.near = 1;
keyLight.shadow.camera.far = 20;
keyLight.shadow.camera.left = keyLight.shadow.camera.bottom = -5;
keyLight.shadow.camera.right = keyLight.shadow.camera.top = 5;
keyLight.shadow.bias = -0.001;
scene.add(keyLight);
// ── 补光(蓝色点光源,左侧)──
const fillLight = new THREE.PointLight(0x0066ff, 2, 10);
fillLight.position.set(-4, 2, 2);
scene.add(fillLight);
// ── 轮廓光(背面)──
const rimLight = new THREE.SpotLight(0x00aaff, 5, 15, Math.PI/5, 0.3);
rimLight.position.set(0, 4, -5);
rimLight.target = product;
scene.add(rimLight, rimLight.target);
// ── 自动旋转 + OrbitControls ──
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.minDistance = 3;
controls.maxDistance = 15;
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const t = clock.getElapsedTime();
product.rotation.y = t * 0.4;
controls.update();
renderer.render(scene, camera);
}
animate();
三点打光法:专业摄影/3D 渲染中常用的基础布光方案,包含:
• 主光(Key Light):主要光源,决定基本光影结构,通常较强
• 补光(Fill Light):填补暗部,防止阴影太黑,通常颜色与主光互补
• 轮廓光(Rim/Back Light):从物体背面打光,分离物体与背景,增加立体感
本章小结:光照是决定 3D 场景品质的关键因素之一。实践中建议:环境光 0.3-0.5 亮度 + 方向光作为主光源 + 1-2 个补光点;阴影只给主光源开启;开发阶段添加光照辅助器,上线前移除。