学习Three.js--光源Light+轨道控制器OrbitControls
前置核心说明
Three.js 的 3D 世界默认处于全黑状态,想要让「受光照影响的材质」显示出真实的光影效果,必须手动添加光源;而轨道控制器(OrbitControls)是 Three.js 官方提供的核心交互插件,能让你通过鼠标/触摸自由操控相机视角,是 3D 场景交互的标配。
核心规则
-
材质与光照的对应关系
材质类型 是否受光照影响 核心特点 MeshBasicMaterial 不受 纯色/纹理显示,无需光源,仅用于调试/简单UI MeshLambertMaterial 受 漫反射光照,无高光,哑光质感 MeshPhongMaterial 受 高光光照,模拟光泽质感 MeshStandardMaterial 受 PBR物理光照,真实质感(推荐首选) MeshPhysicalMaterial 受 高级PBR光照,极致写实 -
光源通用规则:
- 所有光源均继承自
THREE.Light,共用「颜色、强度」核心属性; - 光源创建后必须通过
scene.add(光源)添加到场景才会生效; - 除环境光外,其他光源(点/聚光/平行光)需设置
position或target确定光照方向/位置。
- 所有光源均继承自
一、Three.js 核心光源详解(参数+用法+效果)
1. 环境光 | AmbientLight(打底光,必加)
核心概念
AmbientLight 是全局均匀照明光源,无方向、无阴影、无衰减,作用是「给场景所有物体打基础亮度」,消除纯黑区域,但不会产生明暗对比和立体感,必须配合其他光源(如平行光)使用。
创建语法 & 完整参数
// 语法:new THREE.AmbientLight(颜色, 光照强度)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight); // 必须添加到场景才生效
| 参数名 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
color | Number/String/THREE.Color | 0xffffff | 光源颜色(支持16进制/字符串/Color对象) |
intensity | Number | 1 | 光照强度(0=无光,1=标准强度,建议0.2~0.8) |
核心特点 & 效果
- 无位置属性(全局生效),无法设置
position; - 效果:所有物体被均匀照亮,无明暗差异,单独使用会让物体像“平面贴纸”,无立体感;
- 适用场景:作为「打底光」,配合主光源(平行光/聚光灯)使用,避免物体阴影区域纯黑。
2. 平行光 | DirectionalLight(主光源首选)
核心概念
DirectionalLight 模拟太阳光,光线平行发射,无衰减(距离不影响亮度),有明确的照射方向,能产生自然的明暗对比和阴影,是 3D 场景「主光源」的最优选择。
创建语法 & 完整参数
// 语法:new THREE.DirectionalLight(颜色, 光照强度)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
// 核心:设置光源位置(决定光照方向),默认在原点(0,0,0)
dirLight.position.set(5, 5, 5); // 光源在(5,5,5),朝向场景原点照射
// 可选:设置光照目标(默认朝向原点,可指定物体)
dirLight.target = cubeMesh; // 光照朝向立方体
// 可选:开启阴影投射(实现物体阴影效果)
dirLight.castShadow = true;
scene.add(dirLight);
| 参数名 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
color | Number/String/THREE.Color | 0xffffff | 光源颜色 |
intensity | Number | 1 | 光照强度(建议0.8~1.2) |
核心属性
| 属性名 | 说明 | 示例 |
|---|---|---|
position | 光源位置(THREE.Vector3),决定光照方向 | dirLight.position.set(5,5,5) |
target | 光照目标点/物体(默认原点) | dirLight.target = mesh |
castShadow | 是否投射阴影(默认false) | dirLight.castShadow = true |
shadow | 阴影配置对象(如阴影分辨率) | dirLight.shadow.mapSize.set(2048,2048) |
效果 & 适用场景
- 效果:光线平行,物体朝向光源的面亮,背向面暗,立体感极强;
- 适用:场景主光源(模拟太阳光)、户外场景、全局照明。
3. 点光源 | PointLight(模拟灯泡/火把)
核心概念
PointLight 模拟灯泡、蜡烛、火把,从一个点向「所有方向」发射光线,有衰减(距离越远,光线越暗),能产生真实的距离明暗变化。
创建语法 & 完整参数
// 语法:new THREE.PointLight(颜色, 强度, 光照距离, 衰减指数)
const pointLight = new THREE.PointLight(0xffaa00, 1.5, 10, 2);
// 核心:设置光源位置(默认原点)
pointLight.position.set(2, 2, 2);
// 可选:开启阴影投射
pointLight.castShadow = true;
scene.add(pointLight);
| 参数名 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
color | Number/String/THREE.Color | 0xffffff | 光源颜色 |
intensity | Number | 1 | 光照强度 |
distance | Number | 0 | 光照最大距离(0=无限远),超过该距离无光照 |
decay | Number | 1 | 衰减指数(物理正确值为2,值越大衰减越快) |
核心效果
- 效果:光源中心最亮,向四周逐渐变暗,距离越远越暗;
- 适用:灯泡、火把、台灯、局部点照明场景。
4. 聚光灯光源 | SpotLight(模拟手电筒/射灯)
核心概念
SpotLight 模拟手电筒、舞台射灯、汽车大灯,从一个点向「指定方向」发射「锥形光线」,有角度、有衰减,边缘可模糊,阴影效果精准。
创建语法 & 完整参数
// 语法:new THREE.SpotLight(颜色, 强度, 距离, 锥形角度, 边缘模糊度, 衰减指数)
const spotLight = new THREE.SpotLight(0xffffff, 2, 15, Math.PI/6, 0.2, 2);
// 核心:设置光源位置+目标点
spotLight.position.set(0, 8, 0); // 光源在顶部
spotLight.target = cubeMesh; // 照射立方体
// 可选:开启阴影
spotLight.castShadow = true;
scene.add(spotLight);
| 参数名 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
color | Number/String/THREE.Color | 0xffffff | 光源颜色 |
intensity | Number | 1 | 光照强度 |
distance | Number | 0 | 光照最大距离 |
angle | Number(弧度) | Math.PI/3 | 锥形光照角度(越小,光束越集中) |
penumbra | Number(0~1) | 0 | 光线边缘模糊度(值越大,边缘越柔) |
decay | Number | 1 | 衰减指数 |
核心效果 & 适用场景
- 效果:锥形光照区域,中心亮、边缘渐暗,光束范围可控;
- 适用:手电筒、舞台射灯、汽车大灯、局部精准照明。
二、轨道控制器 OrbitControls(交互核心)
2.1 核心说明
OrbitControls 是 Three.js 官方提供的相机交互插件,无需手动编写鼠标/触摸事件,即可实现相机视角的「旋转、缩放、平移」,是 3D 场景交互的标配,支持鼠标和移动端触摸。
2.2 交互效果(固定规则,无需配置)
| 操作方式 | 交互效果 |
|---|---|
| 鼠标左键拖拽 | 相机围绕目标点旋转(场景整体旋转) |
| 鼠标滚轮滚动 | 相机靠近/远离目标点(场景缩放) |
| 鼠标右键拖拽 | 相机平移(场景整体平移) |
| 移动端单指拖拽 | 旋转视角 |
| 移动端双指缩放 | 场景缩放 |
2.3 引入方式(两种场景,全覆盖)
方式1:CDN 引入(新手/快速测试,推荐)
直接在 HTML 中引入,无需安装依赖:
<!-- 先引入Three.js核心库,再引入OrbitControls -->
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js'
import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js"
</script>
方式2:NPM 引入(工程化项目,Vue/React/Vite)
# 安装Three.js(已包含OrbitControls)
npm install three --save
// 模块化导入
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
2.4 完整使用语法 & 核心参数
基础使用(最简版)
// 语法:new OrbitControls(要控制的相机, 监听的DOM元素)
const controls = new OrbitControls(camera, renderer.domElement);
// 核心:开启阻尼效果(视角移动更顺滑,有惯性,体验更佳)
controls.enableDamping = true;
// 阻尼系数(越小,惯性越强,越顺滑)
controls.dampingFactor = 0.05;
// 动画循环(必须!enableDamping=true时,需实时更新控制器)
function animate() {
requestAnimationFrame(animate);
controls.update(); // 关键:更新控制器状态
renderer.render(scene, camera);
}
animate();
核心配置参数(高频使用)
| 参数名 | 类型 | 默认值 | 核心说明 |
|---|---|---|---|
enableDamping | Boolean | false | 是否开启阻尼(顺滑惯性),推荐设为true |
dampingFactor | Number | 0.05 | 阻尼系数(0.01~0.1,越小越顺滑) |
enableRotate | Boolean | true | 是否允许旋转视角 |
rotateSpeed | Number | 1.0 | 旋转速度(值越大,旋转越快) |
enableZoom | Boolean | true | 是否允许缩放视角 |
zoomSpeed | Number | 1.0 | 缩放速度 |
enablePan | Boolean | true | 是否允许平移视角 |
panSpeed | Number | 1.0 | 平移速度 |
minDistance | Number | 0 | 相机最小缩放距离(防止缩太近) |
maxDistance | Number | Infinity | 相机最大缩放距离(防止缩太远) |
minPolarAngle | Number(弧度) | 0 | 垂直旋转最小角度(防止视角翻到场景下方) |
maxPolarAngle | Number(弧度) | Math.PI | 垂直旋转最大角度 |
核心方法
| 方法名 | 说明 | 示例 |
|---|---|---|
update() | 更新控制器状态(阻尼生效必备) | controls.update() |
reset() | 重置相机视角到初始状态 | controls.reset() |
dispose() | 销毁控制器(释放内存) | controls.dispose() |
2.5 关键注意事项(避坑必看)
- 阻尼效果必须加动画循环:
enableDamping = true时,必须在requestAnimationFrame中调用controls.update(),否则阻尼无效,视角卡顿; - 无需监听change事件:早期教程会监听
controls.addEventListener('change', () => renderer.render(...)),但动画循环本身会实时渲染,无需额外监听,反而会增加性能开销; - DOM元素绑定:第二个参数必须传
renderer.domElement(渲染器的canvas画布),否则控制器无法监听鼠标事件; - 视角限制:通过
minDistance/maxDistance限制缩放范围,避免用户缩放过度导致看不到场景。
三、完整示例代码(光源+OrbitControls 综合使用)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Three.js 光源+轨道控制器</title>
<style>body { margin: 0; overflow: hidden; }</style>
</head>
<body>
<script type="module">
import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/build/three.module.js'
import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js"
// 1. 创建三大核心
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true }); // 抗锯齿
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true; // 开启全局阴影支持
document.body.appendChild(renderer.domElement);
// 2. 创建几何体+材质+网格(PBR材质,受光照影响)
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5, // 粗糙度
metalness: 0.2 // 金属度
});
const cube = new THREE.Mesh(geometry, material);
cube.castShadow = true; // 立方体投射阴影
cube.receiveShadow = true; // 立方体接收阴影
scene.add(cube);
// 3. 创建地面(接收阴影)
const groundGeo = new THREE.PlaneGeometry(10, 10);
const groundMat = new THREE.MeshStandardMaterial({ color: 0xcccccc });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2; // 旋转为地面
ground.position.y = -3;
ground.receiveShadow = true; // 地面接收阴影
scene.add(ground);
// 4. 添加光源(环境光+平行光+点光源)
// 4.1 环境光(打底)
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
// 4.2 平行光(主光源,模拟太阳)
const dirLight = new THREE.DirectionalLight(0xffffff, 1.2);
dirLight.position.set(5, 8, 5);
dirLight.castShadow = true;
dirLight.shadow.mapSize.set(2048, 2048); // 提高阴影分辨率
scene.add(dirLight);
// 4.3 点光源(模拟灯泡)
const pointLight = new THREE.PointLight(0xffaa00, 1.5, 10, 2);
pointLight.position.set(3, 2, 3);
pointLight.castShadow = true;
scene.add(pointLight);
// 5. 设置相机位置
camera.position.z = 8;
// 6. 创建轨道控制器(核心交互)
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 阻尼顺滑
controls.dampingFactor = 0.05;
controls.minDistance = 3; // 最小缩放距离
controls.maxDistance = 20; // 最大缩放距离
// 7. 动画循环
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01; // 立方体旋转
cube.rotation.y += 0.01;
controls.update(); // 更新控制器(阻尼生效)
renderer.render(scene, camera);
}
animate();
// 8. 窗口适配
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
示例效果
- 场景中有绿色立方体和灰色地面,立方体缓慢旋转;
- 平行光模拟太阳光,立方体产生自然阴影;
- 点光源模拟灯泡,立方体靠近光源的区域更亮;
- 鼠标操作:左键旋转视角、滚轮缩放、右键平移,视角移动顺滑有惯性;
- 窗口缩放时,场景自动适配尺寸。
核心总结
- 光源使用原则:
- 受光照材质必须加光源,「环境光(打底)+ 主光源(平行光/聚光灯)」是最优组合;
- 平行光是场景主光源首选,点光源/聚光灯用于局部照明;
- 开启阴影需同时设置「光源.castShadow + 物体.castShadow/receiveShadow + 渲染器.shadowMap.enabled」。
- OrbitControls 核心:
- 阻尼效果(enableDamping=true)是提升交互体验的关键,必须在动画循环中调用
controls.update(); - 通过
min/maxDistance限制缩放范围,避免视角失控; - 无需监听
change事件,动画循环实时渲染即可。
- 阻尼效果(enableDamping=true)是提升交互体验的关键,必须在动画循环中调用
- 性能优化:
- 光源阴影分辨率不宜过高(如2048足够),否则影响性能;
- 非必要场景关闭阴影,减少计算开销。