threejs-lighting

threejs-lighting

熱門

Three.js 燈光 - 燈光類型、陰影、環境照明。用於添加燈光、配置陰影、設定 IBL 或最佳化燈光效能時使用。

2616星標
300分支
更新於 2026/7/9
SKILL.md
唯讀
名稱
threejs-lighting
描述

Three.js 燈光 - 燈光類型、陰影、環境照明。用於添加燈光、配置陰影、設定 IBL 或最佳化燈光效能時使用。

Three.js 燈光

快速開始

import * as THREE from "three";

// 基本燈光設定
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);

燈光類型概覽

燈光 描述 陰影支援 效能成本
AmbientLight 均勻照亮所有物體 極低
HemisphereLight 天空/地面漸層 極低
DirectionalLight 平行光(太陽)
PointLight 全方向(燈泡)
SpotLight 圓錐形
RectAreaLight 區域光(窗戶) 無*

*RectAreaLight 陰影需要自訂解決方案

AmbientLight

均勻照亮所有物體。無方向、無陰影。

// AmbientLight(color, intensity)
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);

// 執行時修改
ambient.color.set(0xffffcc);
ambient.intensity = 0.3;

HemisphereLight

從天空到地面的顏色漸層。適合戶外場景。

// HemisphereLight(skyColor, groundColor, intensity)
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x8b4513, 0.6);
hemi.position.set(0, 50, 0);
scene.add(hemi);

// 屬性
hemi.color; // 天空顏色
hemi.groundColor; // 地面顏色
hemi.intensity;

DirectionalLight

平行光線。模擬遠距離光源(太陽)。

// DirectionalLight(color, intensity)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 5);

// 燈光指向目標(預設:0, 0, 0)
dirLight.target.position.set(0, 0, 0);
scene.add(dirLight.target);

scene.add(dirLight);

DirectionalLight 陰影

dirLight.castShadow = true;

// 陰影貼圖大小(越大越銳利,效能成本越高)
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;

// 陰影攝影機(正交)
dirLight.shadow.camera.near = 0.5;
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;

// 陰影柔化
dirLight.shadow.radius = 4; // 模糊半徑(僅限 PCFSoftShadowMap)

// 陰影偏移(修正陰影痤瘡)
dirLight.shadow.bias = -0.0001;
dirLight.shadow.normalBias = 0.02;

// 輔助工具:可視化陰影攝影機
const helper = new THREE.CameraHelper(dirLight.shadow.camera);
scene.add(helper);

PointLight

從一個點向所有方向發射光線。就像燈泡。

// PointLight(color, intensity, distance, decay)
const pointLight = new THREE.PointLight(0xffffff, 1, 100, 2);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);

// 屬性
pointLight.distance; // 最大範圍(0 = 無限)
pointLight.decay; // 光線衰減(物理正確 = 2)

PointLight 陰影

pointLight.castShadow = true;
pointLight.shadow.mapSize.width = 1024;
pointLight.shadow.mapSize.height = 1024;

// 陰影攝影機(透視 - 立方體貼圖的 6 個方向)
pointLight.shadow.camera.near = 0.5;
pointLight.shadow.camera.far = 50;

pointLight.shadow.bias = -0.005;

SpotLight

圓錐形光線。像手電筒或舞台燈。

// SpotLight(color, intensity, distance, angle, penumbra, decay)
const spotLight = new THREE.SpotLight(0xffffff, 1, 100, Math.PI / 6, 0.5, 2);
spotLight.position.set(0, 10, 0);

// 目標(燈光指向此處)
spotLight.target.position.set(0, 0, 0);
scene.add(spotLight.target);

scene.add(spotLight);

// 屬性
spotLight.angle; // 圓錐角度(弧度,最大 Math.PI/2)
spotLight.penumbra; // 邊緣柔化(0-1)
spotLight.distance; // 範圍
spotLight.decay; // 衰減

SpotLight 陰影

spotLight.castShadow = true;
spotLight.shadow.mapSize.width = 1024;
spotLight.shadow.mapSize.height = 1024;

// 陰影攝影機(透視)
spotLight.shadow.camera.near = 0.5;
spotLight.shadow.camera.far = 50;
spotLight.shadow.camera.fov = 30;

spotLight.shadow.bias = -0.0001;

// 焦點(影響陰影投射)
spotLight.shadow.focus = 1;

RectAreaLight

矩形區域光。非常適合柔和、真實的照明。

import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js";
import { RectAreaLightUniformsLib } from "three/examples/jsm/lights/RectAreaLightUniformsLib.js";

// 必須先初始化 uniform
RectAreaLightUniformsLib.init();

// RectAreaLight(color, intensity, width, height)
const rectLight = new THREE.RectAreaLight(0xffffff, 5, 4, 2);
rectLight.position.set(0, 5, 0);
rectLight.lookAt(0, 0, 0);
scene.add(rectLight);

// 輔助工具
const helper = new RectAreaLightHelper(rectLight);
rectLight.add(helper);

// 注意:僅適用於 MeshStandardMaterial 和 MeshPhysicalMaterial
// 原生不支援投射陰影

陰影設定

啟用陰影

// 1. 在渲染器上啟用
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 陰影貼圖類型:
// THREE.BasicShadowMap - 最快,低品質
// THREE.PCFShadowMap - 預設,已濾波
// THREE.PCFSoftShadowMap - 邊緣更柔和
// THREE.VSMShadowMap - 方差陰影貼圖

// 2. 在燈光上啟用
light.castShadow = true;

// 3. 在物體上啟用
mesh.castShadow = true;
mesh.receiveShadow = true;

// 地面
floor.receiveShadow = true;
floor.castShadow = false; // 地板通常設為 false

最佳化陰影

// 緊湊的陰影攝影機視錐
const d = 10;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
dirLight.shadow.camera.near = 0.5;
dirLight.shadow.camera.far = 30;

// 修正陰影痤瘡
dirLight.shadow.bias = -0.0001; // 深度偏移
dirLight.shadow.normalBias = 0.02; // 沿法線偏移

// 陰影貼圖大小(平衡品質與效能)
// 512 - 低品質
// 1024 - 中品質
// 2048 - 高品質
// 4096 - 極高品質(效能成本高)

接觸陰影(偽造、快速)

import { ContactShadows } from "three/examples/jsm/objects/ContactShadows.js";

const contactShadows = new ContactShadows({
  resolution: 512,
  blur: 2,
  opacity: 0.5,
  scale: 10,
  position: [0, 0, 0],
});
scene.add(contactShadows);

燈光輔助工具

import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper.js";

// DirectionalLight 輔助工具
const dirHelper = new THREE.DirectionalLightHelper(dirLight, 5);
scene.add(dirHelper);

// PointLight 輔助工具
const pointHelper = new THREE.PointLightHelper(pointLight, 1);
scene.add(pointHelper);

// SpotLight 輔助工具
const spotHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotHelper);

// Hemisphere 輔助工具
const hemiHelper = new THREE.HemisphereLightHelper(hemiLight, 5);
scene.add(hemiHelper);

// RectAreaLight 輔助工具
const rectHelper = new RectAreaLightHelper(rectLight);
rectLight.add(rectHelper);

// 燈光變更時更新輔助工具
dirHelper.update();
spotHelper.update();

環境照明(IBL)

使用 HDR 環境貼圖的影像基礎照明。

import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js";

const rgbeLoader = new RGBELoader();
rgbeLoader.load("environment.hdr", (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;

  // 設定為場景環境(影響所有 PBR 材質)
  scene.environment = texture;

  // 可選:也用作背景
  scene.background = texture;
  scene.backgroundBlurriness = 0; // 0-1,模糊背景
  scene.backgroundIntensity = 1;
});

// PMREMGenerator 用於更好的反射
const pmremGenerator = new THREE.PMREMGenerator(renderer);
pmremGenerator.compileEquirectangularShader();

rgbeLoader.load("environment.hdr", (texture) => {
  const envMap = pmremGenerator.fromEquirectangular(texture).texture;
  scene.environment = envMap;
  texture.dispose();
  pmremGenerator.dispose();
});

立方體紋理環境

const cubeLoader = new THREE.CubeTextureLoader();
const envMap = cubeLoader.load([
  "px.jpg",
  "nx.jpg",
  "py.jpg",
  "ny.jpg",
  "pz.jpg",
  "nz.jpg",
]);

scene.environment = envMap;
scene.background = envMap;

光探測(進階)

從空間中的一個點捕捉照明,用於環境照明。

import { LightProbeGenerator } from "three/examples/jsm/lights/LightProbeGenerator.js";

// 從立方體紋理生成
const lightProbe = new THREE.LightProbe();
scene.add(lightProbe);

lightProbe.copy(LightProbeGenerator.fromCubeTexture(cubeTexture));

// 或從渲染目標生成
const cubeCamera = new THREE.CubeCamera(
  0.1,
  100,
  new THREE.WebGLCubeRenderTarget(256),
);
cubeCamera.update(renderer, scene);
lightProbe.copy(
  LightProbeGenerator.fromCubeRenderTarget(renderer, cubeCamera.renderTarget),
);

常見燈光設定

三點照明

// 主光(主要燈光)
const keyLight = new THREE.DirectionalLight(0xffffff, 1);
keyLight.position.set(5, 5, 5);
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);

// 環境補光
const ambient = new THREE.AmbientLight(0x404040, 0.3);
scene.add(ambient);

戶外日光

// 太陽
const sun = new THREE.DirectionalLight(0xffffcc, 1.5);
sun.position.set(50, 100, 50);
sun.castShadow = true;
scene.add(sun);

// 天空環境
const hemi = new THREE.HemisphereLight(0x87ceeb, 0x8b4513, 0.6);
scene.add(hemi);

室內攝影棚

// 多個區域光
RectAreaLightUniformsLib.init();

const light1 = new THREE.RectAreaLight(0xffffff, 5, 2, 2);
light1.position.set(3, 3, 3);
light1.lookAt(0, 0, 0);
scene.add(light1);

const light2 = new THREE.RectAreaLight(0xffffff, 3, 2, 2);
light2.position.set(-3, 3, 3);
light2.lookAt(0, 0, 0);
scene.add(light2);

// 環境補光
const ambient = new THREE.AmbientLight(0x404040, 0.2);
scene.add(ambient);

燈光動畫

const clock = new THREE.Clock();

function animate() {
  const time = clock.getElapsedTime();

  // 燈光繞場景旋轉
  light.position.x = Math.cos(time) * 5;
  light.position.z = Math.sin(time) * 5;

  // 脈衝強度
  light.intensity = 1 + Math.sin(time * 2) * 0.5;

  // 顏色循環
  light.color.setHSL((time * 0.1) % 1, 1, 0.5);

  // 如有使用輔助工具,更新之
  lightHelper.update();
}

效能提示

  1. 限制燈光數量:每個燈光都會增加著色器複雜度
  2. 使用烘焙照明:對於靜態場景,烘焙到紋理
  3. 較小的陰影貼圖:512-1024 通常足夠
  4. 緊湊的陰影視錐:僅涵蓋需要的區域
  5. 停用不必要的陰影:並非所有燈光都需要陰影
  6. 使用燈光圖層:將物體排除在某些燈光之外
// 燈光圖層
light.layers.set(1); // 燈光僅影響圖層 1
mesh.layers.enable(1); // 網格位於圖層 1
otherMesh.layers.disable(1); // 其他網格不受影響

// 選擇性陰影
mesh.castShadow = true;
mesh.receiveShadow = true;
decorMesh.castShadow = false; // 小物體通常不需要投射陰影

另請參閱

  • threejs-materials - 材質對光線的反應
  • threejs-textures - 光照貼圖與環境貼圖
  • threejs-postprocessing - 泛光及其他光線效果