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