跳到主要内容

Three.js 知识点总结

Three.js 是一个基于 WebGL 的 3D 图形库,用于在浏览器中创建和显示 3D 场景。

核心概念

三大核心组件

Three.js 的核心由三个组件构成:

  1. Scene(场景) - 3D 世界的容器
  2. Camera(相机) - 定义观察者的视角
  3. Renderer(渲染器) - 将场景渲染到画布上
// 基本结构
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

Scene(场景)

场景是所有对象的容器,用于组织和管理 3D 对象。

const scene = new THREE.Scene();

// 设置背景色
scene.background = new THREE.Color(0x000000);

// 添加雾效果
scene.fog = new THREE.Fog(0x000000, 10, 50);

场景属性

  • background: 场景背景(颜色、纹理或立方体贴图)
  • fog: 雾效果
  • environment: 环境贴图(用于 PBR 材质)

Camera(相机)

PerspectiveCamera(透视相机)

最常用的相机类型,模拟人眼视角。

const camera = new THREE.PerspectiveCamera(
75, // FOV(视野角度)
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
);

camera.position.set(0, 0, 5);
camera.lookAt(0, 0, 0);

OrthographicCamera(正交相机)

正交投影,无透视变形。

const camera = new THREE.OrthographicCamera(
-1, 1, // left, right
1, -1, // top, bottom
0.1, // near
100 // far
);

相机控制

// 使用 OrbitControls 控制相机
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼
controls.dampingFactor = 0.05;

Renderer(渲染器)

WebGLRenderer

使用 WebGL 进行硬件加速渲染。

const renderer = new THREE.WebGLRenderer({
antialias: true, // 抗锯齿
alpha: true, // 透明背景
powerPreference: "high-performance" // 性能偏好
});

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio); // 适配高DPI屏幕
renderer.shadowMap.enabled = true; // 启用阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 阴影类型

渲染循环

function animate() {
requestAnimationFrame(animate);

// 更新控制器
controls.update();

// 渲染场景
renderer.render(scene, camera);
}

animate();

Geometry(几何体)

内置几何体

// 立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);

// 球体
const geometry = new THREE.SphereGeometry(1, 32, 32);

// 平面
const geometry = new THREE.PlaneGeometry(1, 1);

// 圆柱体
const geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);

// 圆环
const geometry = new THREE.TorusGeometry(0.5, 0.2, 16, 100);

// 圆锥
const geometry = new THREE.ConeGeometry(0.5, 1, 32);

// 圆环结
const geometry = new THREE.TorusKnotGeometry(0.5, 0.2, 100, 16);

BufferGeometry

更灵活、性能更好的几何体,适合自定义形状。

const geometry = new THREE.BufferGeometry();

// 定义顶点位置
const vertices = new Float32Array([
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
]);

geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

// 定义索引
const indices = new Uint16Array([0, 1, 2, 2, 3, 0]);
geometry.setIndex(indices);

// 计算法线
geometry.computeVertexNormals();

几何体操作

// 合并几何体
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries([geo1, geo2]);

// 中心化
geometry.center();

// 归一化
geometry.normalize();

// 计算包围盒
geometry.computeBoundingBox();
geometry.computeBoundingSphere();

Material(材质)

基础材质

// MeshBasicMaterial - 不受光照影响
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });

// MeshLambertMaterial - 漫反射材质
const material = new THREE.MeshLambertMaterial({ color: 0x00ff00 });

// MeshPhongMaterial - 高光材质
const material = new THREE.MeshPhongMaterial({
color: 0x00ff00,
shininess: 100,
specular: 0x222222
});

// MeshStandardMaterial - PBR 标准材质
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5
});

// MeshPhysicalMaterial - 物理材质(更真实)
const material = new THREE.MeshPhysicalMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5,
clearcoat: 1.0,
clearcoatRoughness: 0.1
});

材质属性

const material = new THREE.MeshStandardMaterial({
color: 0x00ff00, // 颜色
map: texture, // 漫反射贴图
normalMap: normalTexture, // 法线贴图
roughnessMap: roughnessTexture, // 粗糙度贴图
metalnessMap: metalnessTexture, // 金属度贴图
aoMap: aoTexture, // 环境光遮蔽贴图
emissive: 0x000000, // 自发光颜色
emissiveMap: emissiveTexture, // 自发光贴图
transparent: true, // 启用透明
opacity: 0.5, // 透明度
side: THREE.DoubleSide, // 渲染面(FrontSide/BackSide/DoubleSide)
wireframe: false, // 线框模式
flatShading: false // 平面着色
});

纹理

// 加载纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg');

// 纹理设置
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(2, 2);
texture.offset.set(0.5, 0.5);
texture.rotation = Math.PI / 4;

// 立方体贴图
const cubeTextureLoader = new THREE.CubeTextureLoader();
const envMap = cubeTextureLoader.load([
'px.jpg', 'nx.jpg',
'py.jpg', 'ny.jpg',
'pz.jpg', 'nz.jpg'
]);
scene.background = envMap;
material.envMap = envMap;

Mesh(网格)

网格是几何体和材质的组合。

const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(geometry, material);

mesh.position.set(0, 0, 0);
mesh.rotation.set(0, Math.PI / 4, 0);
mesh.scale.set(1, 1, 1);

scene.add(mesh);

对象变换

// 位置
mesh.position.x = 1;
mesh.position.set(1, 2, 3);

// 旋转(弧度制)
mesh.rotation.x = Math.PI / 4;
mesh.rotation.set(0, Math.PI / 4, 0);

// 缩放
mesh.scale.set(2, 2, 2);

// 使用四元数旋转(避免万向锁)
mesh.quaternion.setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI / 4);

Light(光照)

光源类型

// 环境光 - 均匀照亮所有物体
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);

// 方向光 - 平行光(如太阳光)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 5, 5);
directionalLight.castShadow = true;
scene.add(directionalLight);

// 点光源 - 从一点向四周发射
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 10, 0);
pointLight.castShadow = true;
scene.add(pointLight);

// 聚光灯 - 圆锥形光束
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(0, 10, 0);
spotLight.angle = Math.PI / 4;
spotLight.penumbra = 0.1;
spotLight.castShadow = true;
scene.add(spotLight);

// 半球光 - 模拟天空和地面
const hemisphereLight = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(hemisphereLight);

// 矩形光 - 面光源(用于 PBR)
const rectLight = new THREE.RectAreaLight(0xffffff, 1, 10, 10);
rectLight.position.set(5, 5, 5);
scene.add(rectLight);

阴影

// 启用阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 光源投射阴影
light.castShadow = true;
light.shadow.mapSize.width = 2048;
light.shadow.mapSize.height = 2048;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 50;

// 物体接收/投射阴影
mesh.castShadow = true;
mesh.receiveShadow = true;

Animation(动画)

使用 requestAnimationFrame

function animate() {
requestAnimationFrame(animate);

// 旋转动画
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.01;

renderer.render(scene, camera);
}

animate();

使用 GSAP 动画库

import gsap from 'gsap';

gsap.to(mesh.rotation, {
x: Math.PI * 2,
y: Math.PI * 2,
duration: 2,
repeat: -1,
ease: "none"
});

使用 Clock 控制时间

const clock = new THREE.Clock();

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

mesh.rotation.y = elapsedTime;
camera.position.x = Math.sin(elapsedTime) * 3;
camera.position.z = Math.cos(elapsedTime) * 3;
camera.lookAt(mesh.position);

renderer.render(scene, camera);
requestAnimationFrame(animate);
}

Loaders(加载器)

GLTFLoader - 加载 3D 模型

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

const loader = new GLTFLoader();
loader.load(
'path/to/model.gltf',
(gltf) => {
const model = gltf.scene;
scene.add(model);
},
(progress) => {
console.log('加载进度:', progress.loaded / progress.total * 100 + '%');
},
(error) => {
console.error('加载错误:', error);
}
);

TextureLoader - 加载纹理

const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load('path/to/texture.jpg');

CubeTextureLoader - 加载立方体贴图

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

FontLoader - 加载字体

import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js';

const fontLoader = new FontLoader();
fontLoader.load('path/to/font.json', (font) => {
const textGeometry = new TextGeometry('Hello Three.js', {
font: font,
size: 0.5,
height: 0.2,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
bevelOffset: 0,
bevelSegments: 5
});

const textMesh = new THREE.Mesh(textGeometry, material);
scene.add(textMesh);
});

性能优化

几何体合并

import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';

const geometries = [];
for (let i = 0; i < 1000; i++) {
const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
geometry.translate(Math.random() * 10, Math.random() * 10, Math.random() * 10);
geometries.push(geometry);
}

const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries);
const mesh = new THREE.Mesh(mergedGeometry, material);
scene.add(mesh);

实例化渲染

const geometry = new THREE.BoxGeometry(0.1, 0.1, 0.1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.InstancedMesh(geometry, material, 1000);

const matrix = new THREE.Matrix4();
for (let i = 0; i < 1000; i++) {
matrix.setPosition(
Math.random() * 10,
Math.random() * 10,
Math.random() * 10
);
mesh.setMatrixAt(i, matrix);
}

scene.add(mesh);

LOD(细节层次)

const lod = new THREE.LOD();

// 添加不同细节级别的模型
lod.addLevel(highDetailMesh, 0);
lod.addLevel(mediumDetailMesh, 50);
lod.addLevel(lowDetailMesh, 100);

scene.add(lod);

视锥体剔除

// 自动启用,但需要正确设置几何体的包围盒
geometry.computeBoundingSphere();

减少绘制调用

  • 合并几何体
  • 使用实例化渲染
  • 使用纹理图集
  • 减少材质数量

常用工具和技巧

Raycaster(射线投射)

用于鼠标拾取和碰撞检测。

const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);

if (intersects.length > 0) {
// 处理相交
console.log(intersects[0].object);
}
}

window.addEventListener('mousemove', onMouseMove);

后处理效果

import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';

const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));

const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // 强度
0.4, // 半径
0.85 // 阈值
);
composer.addPass(bloomPass);

// 在渲染循环中使用
function animate() {
composer.render();
requestAnimationFrame(animate);
}

响应式处理

function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}

window.addEventListener('resize', onWindowResize);

坐标系统

  • 世界坐标: 场景的全局坐标系
  • 局部坐标: 对象自身的坐标系
  • 屏幕坐标: 2D 屏幕坐标系
// 世界坐标转屏幕坐标
const vector = mesh.position.clone();
vector.project(camera);

const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
const y = (vector.y * -0.5 + 0.5) * window.innerHeight;

最佳实践

  1. 资源管理: 及时释放不需要的几何体、材质和纹理

    geometry.dispose();
    material.dispose();
    texture.dispose();
  2. 使用对象池: 复用对象以减少垃圾回收

  3. 合理使用阴影: 阴影计算消耗大,只对必要对象启用

  4. 优化纹理大小: 使用合适的纹理分辨率

  5. 使用压缩纹理: 使用 KTX2/Basis 压缩格式

  6. 批处理: 合并相同材质的几何体

  7. 使用 Web Workers: 将复杂计算移到 Worker 线程

  8. 性能监控: 使用 Stats.js 监控帧率

import Stats from 'three/examples/jsm/libs/stats.module.js';

const stats = new Stats();
document.body.appendChild(stats.dom);

function animate() {
stats.update();
// ... 渲染代码
}

常用资源

版本说明

  • r150+: 使用 ES6 模块,需要构建工具
  • r140 及以下: 支持直接 script 标签引入
// 现代方式(推荐)
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

// 传统方式
<script src="https://cdn.jsdelivr.net/npm/three@0.150.0/build/three.min.js"></script>