Three.js 光照 #
光照是 3D 场景中最重要的元素之一,它决定了物体的明暗、阴影和整体氛围。Three.js 提供了多种光源类型,可以模拟各种真实世界的照明效果。
光照概述 #
text
┌─────────────────────────────────────────────────────────────┐
│ Three.js 光源分类 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 基础光源 │ │ 点光源类 │ │ 区域光源 │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │AmbientLight │ │ PointLight │ │RectAreaLight│ │
│ │DirectionalLt│ │ SpotLight │ │HemisphereLt │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
基础光源 #
环境光(AmbientLight) #
环境光均匀照亮场景中的所有物体,没有方向,不会产生阴影。
javascript
const ambientLight = new THREE.AmbientLight(
0x404040, // color:颜色
1.0 // intensity:强度
);
scene.add(ambientLight);
特点 #
- 无方向性
- 不产生阴影
- 均匀照亮所有物体
- 常作为基础照明
使用场景 #
javascript
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambientLight);
平行光(DirectionalLight) #
平行光模拟太阳光,光线平行且均匀,可以产生阴影。
javascript
const directionalLight = new THREE.DirectionalLight(
0xffffff, // color:颜色
1.0 // intensity:强度
);
directionalLight.position.set(5, 5, 5);
directionalLight.target.position.set(0, 0, 0);
scene.add(directionalLight);
scene.add(directionalLight.target);
属性说明 #
| 属性 | 类型 | 描述 |
|---|---|---|
| color | Color | 光源颜色 |
| intensity | Number | 光照强度 |
| position | Vector3 | 光源位置 |
| target | Object3D | 照射目标 |
| castShadow | Boolean | 是否投射阴影 |
| shadow | Shadow | 阴影配置 |
阴影配置 #
javascript
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
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;
scene.add(directionalLight);
点光源类 #
点光源(PointLight) #
点光源从一个点向所有方向发射光线,类似灯泡。
javascript
const pointLight = new THREE.PointLight(
0xff0000, // color:颜色
1.0, // intensity:强度
100, // distance:影响距离
2.0 // decay:衰减因子
);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);
参数说明 #
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | Number | 1.0 | 光照强度 |
| distance | Number | 0 | 影响距离(0 表示无限) |
| decay | Number | 2.0 | 衰减因子 |
光照衰减 #
text
光照强度 = intensity × (distance / max(distance, 1))^decay
衰减曲线:
强度
▲
│
1 │■
│ ■
│ ■
│ ■
│ ■
│ ■
│ ■
│ ■
└────────► 距离
0 distance
点光源阴影 #
javascript
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 5, 0);
pointLight.castShadow = true;
pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;
pointLight.shadow.camera.near = 0.5;
pointLight.shadow.camera.far = 50;
scene.add(pointLight);
聚光灯(SpotLight) #
聚光灯从一点沿锥形方向发射光线,类似手电筒。
javascript
const spotLight = new THREE.SpotLight(
0xffffff, // color:颜色
1.0, // intensity:强度
100, // distance:影响距离
Math.PI / 6, // angle:照射角度
0.5, // penumbra:边缘柔化
2.0 // decay:衰减因子
);
spotLight.position.set(0, 10, 0);
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight);
scene.add(spotLight.target);
参数说明 #
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| color | Color | 0xffffff | 光源颜色 |
| intensity | Number | 1.0 | 光照强度 |
| distance | Number | 0 | 影响距离 |
| angle | Number | Math.PI/3 | 照射角度(弧度) |
| penumbra | Number | 0.0 | 边缘柔化程度(0-1) |
| decay | Number | 2.0 | 衰减因子 |
聚光灯锥形 #
text
▲ 光源位置
/|\
/ | \
/ | \
/ | \
/ | \
/ | \
/ | \
/ | \
/ angle|angle \
───────────────────
照射范围
聚光灯阴影 #
javascript
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(0, 10, 0);
spotLight.angle = Math.PI / 6;
spotLight.penumbra = 0.5;
spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 2048;
spotLight.shadow.mapSize.height = 2048;
spotLight.shadow.camera.near = 1;
spotLight.shadow.camera.far = 50;
scene.add(spotLight);
区域光源 #
半球光(HemisphereLight) #
半球光模拟天空和地面的环境光,常用于户外场景。
javascript
const hemisphereLight = new THREE.HemisphereLight(
0xffffbb, // skyColor:天空颜色
0x080820, // groundColor:地面颜色
1.0 // intensity:强度
);
scene.add(hemisphereLight);
参数说明 #
| 参数 | 类型 | 描述 |
|---|---|---|
| skyColor | Color | 天空颜色 |
| groundColor | Color | 地面颜色 |
| intensity | Number | 光照强度 |
效果示意 #
text
天空颜色(skyColor)
═════════════════════════
▲
/|\
/ | \
/ | \
/ | \
/ | \
/ | \
/ | \
/ | \
▼ | ▼
═════════════════════════
地面颜色(groundColor)
矩形区域光(RectAreaLight) #
矩形区域光模拟从矩形区域发出的光,如窗户、电视屏幕等。
javascript
import { RectAreaLightUniformsLib } from 'three/addons/lights/RectAreaLightUniformsLib.js';
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
RectAreaLightUniformsLib.init();
const rectAreaLight = new THREE.RectAreaLight(
0xffffff, // color:颜色
5.0, // intensity:强度
2, // width:宽度
2 // height:高度
);
rectAreaLight.position.set(0, 5, 0);
rectAreaLight.lookAt(0, 0, 0);
scene.add(rectAreaLight);
const rectLightHelper = new RectAreaLightHelper(rectAreaLight);
rectAreaLight.add(rectLightHelper);
注意事项 #
- 只支持 MeshStandardMaterial 和 MeshPhysicalMaterial
- 不支持阴影
- 需要初始化 RectAreaLightUniformsLib
光源辅助器 #
使用辅助器可视化光源 #
javascript
import {
DirectionalLightHelper,
PointLightHelper,
SpotLightHelper,
HemisphereLightHelper
} from 'three/addons/helpers/Helpers.js';
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
const directionalLightHelper = new DirectionalLightHelper(
directionalLight,
1, // size:辅助器大小
0xff0000 // color:辅助器颜色
);
scene.add(directionalLightHelper);
const pointLight = new THREE.PointLight(0xff0000, 1, 100);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);
const pointLightHelper = new PointLightHelper(
pointLight,
0.5 // sphereSize:球体大小
);
scene.add(pointLightHelper);
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(0, 10, 0);
scene.add(spotLight);
const spotLightHelper = new SpotLightHelper(spotLight);
scene.add(spotLightHelper);
const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(hemisphereLight);
const hemisphereLightHelper = new HemisphereLightHelper(
hemisphereLight,
1 // size:辅助器大小
);
scene.add(hemisphereLightHelper);
阴影系统 #
启用阴影 #
javascript
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xff6b6b });
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true;
cube.receiveShadow = true;
scene.add(cube);
const planeGeometry = new THREE.PlaneGeometry(10, 10);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.position.y = -0.5;
plane.receiveShadow = true;
scene.add(plane);
阴影类型 #
javascript
renderer.shadowMap.type = THREE.BasicShadowMap;
renderer.shadowMap.type = THREE.PCFShadowMap;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.shadowMap.type = THREE.VSMShadowMap;
阴影质量优化 #
javascript
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.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.normalBias = 0.02;
directionalLight.shadow.radius = 4;
光源对比表 #
| 光源类型 | 阴影 | 方向性 | 性能 | 适用场景 |
|---|---|---|---|---|
| AmbientLight | ❌ | ❌ | 高 | 基础照明 |
| DirectionalLight | ✅ | ✅ | 中 | 太阳光 |
| PointLight | ✅ | ❌ | 低 | 灯泡、火焰 |
| SpotLight | ✅ | ✅ | 低 | 手电筒、舞台灯 |
| HemisphereLight | ❌ | ❌ | 高 | 户外环境 |
| RectAreaLight | ❌ | ✅ | 低 | 窗户、屏幕 |
光照最佳实践 #
基础三点布光 #
javascript
const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
scene.add(ambientLight);
const keyLight = new THREE.DirectionalLight(0xffffff, 1);
keyLight.position.set(5, 5, 5);
keyLight.castShadow = true;
scene.add(keyLight);
const fillLight = new THREE.DirectionalLight(0xffffff, 0.5);
fillLight.position.set(-5, 3, -5);
scene.add(fillLight);
const backLight = new THREE.DirectionalLight(0xffffff, 0.3);
backLight.position.set(0, 5, -5);
scene.add(backLight);
室内场景布光 #
javascript
const ambientLight = new THREE.AmbientLight(0x404040, 0.2);
scene.add(ambientLight);
const ceilingLight = new THREE.PointLight(0xffffee, 1, 10);
ceilingLight.position.set(0, 3, 0);
ceilingLight.castShadow = true;
scene.add(ceilingLight);
const windowLight = new THREE.DirectionalLight(0xffffff, 0.8);
windowLight.position.set(5, 3, 0);
scene.add(windowLight);
户外场景布光 #
javascript
const hemisphereLight = new THREE.HemisphereLight(
0x87ceeb,
0x3d5c3d,
0.6
);
scene.add(hemisphereLight);
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
sunLight.position.set(50, 100, 50);
sunLight.castShadow = true;
sunLight.shadow.mapSize.width = 2048;
sunLight.shadow.mapSize.height = 2048;
sunLight.shadow.camera.near = 0.5;
sunLight.shadow.camera.far = 500;
sunLight.shadow.camera.left = -50;
sunLight.shadow.camera.right = 50;
sunLight.shadow.camera.top = 50;
sunLight.shadow.camera.bottom = -50;
scene.add(sunLight);
光源动画 #
光源移动 #
javascript
const pointLight = new THREE.PointLight(0xff0000, 1, 100);
scene.add(pointLight);
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
pointLight.position.x = Math.sin(time) * 5;
pointLight.position.z = Math.cos(time) * 5;
renderer.render(scene, camera);
}
animate();
光源颜色变化 #
javascript
const pointLight = new THREE.PointLight(0xffffff, 1);
scene.add(pointLight);
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
const color = new THREE.Color();
color.setHSL((time * 0.1) % 1, 1, 0.5);
pointLight.color = color;
renderer.render(scene, camera);
}
animate();
光源强度变化 #
javascript
const pointLight = new THREE.PointLight(0xffffff, 1);
scene.add(pointLight);
function animate() {
requestAnimationFrame(animate);
const time = Date.now() * 0.001;
pointLight.intensity = Math.sin(time) * 0.5 + 1;
renderer.render(scene, camera);
}
animate();
完整示例 #
javascript
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(8, 8, 8);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
const ambientLight = new THREE.AmbientLight(0x404040, 0.3);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 5);
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;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0xff6b6b, 1, 20);
pointLight.position.set(-3, 3, -3);
pointLight.castShadow = true;
scene.add(pointLight);
const spotLight = new THREE.SpotLight(0x6bffb8, 1, 20, Math.PI / 6, 0.5);
spotLight.position.set(3, 5, 3);
spotLight.castShadow = true;
scene.add(spotLight);
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 16);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff6b6b });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.y = 0.5;
sphere.castShadow = true;
sphere.receiveShadow = true;
scene.add(sphere);
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
const boxMaterial = new THREE.MeshStandardMaterial({ color: 0x6bffb8 });
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.set(2, 0.5, 0);
box.castShadow = true;
box.receiveShadow = true;
scene.add(box);
const torusGeometry = new THREE.TorusGeometry(0.4, 0.15, 16, 32);
const torusMaterial = new THREE.MeshStandardMaterial({ color: 0x6bb8ff });
const torus = new THREE.Mesh(torusGeometry, torusMaterial);
torus.position.set(-2, 0.5, 0);
torus.castShadow = true;
torus.receiveShadow = true;
scene.add(torus);
const planeGeometry = new THREE.PlaneGeometry(15, 15);
const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x333333 });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true;
scene.add(plane);
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const elapsedTime = clock.getElapsedTime();
pointLight.position.x = Math.sin(elapsedTime) * 5;
pointLight.position.z = Math.cos(elapsedTime) * 5;
sphere.rotation.y = elapsedTime;
box.rotation.x = elapsedTime * 0.5;
box.rotation.y = elapsedTime * 0.5;
torus.rotation.x = elapsedTime;
controls.update();
renderer.render(scene, camera);
}
animate();
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
下一步 #
现在你已经掌握了光照系统,接下来学习 相机详解,深入了解相机的使用!
最后更新:2026-03-28