跳到主要内容

Three.js 速查表

本文档提供 Three.js 常用 API 和代码片段的快速参考。

初始化

基础设置

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(60, width / height, 0.1, 1000);
camera.position.set(0, 5, 10);

// 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);

// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

// 渲染循环
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
controls.update();
renderer.render(scene, camera);
}
animate();

响应窗口变化

window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});

场景

// 创建场景
const scene = new THREE.Scene();

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

// 背景透明
scene.background = null;

// 雾效 - 线性
scene.fog = new THREE.Fog(color, near, far);

// 雾效 - 指数
scene.fog = new THREE.FogExp2(color, density);

// 环境贴图
scene.environment = texture;

// 添加对象
scene.add(object);

// 移除对象
scene.remove(object);

// 遍历场景
scene.traverse((obj) => { });

// 按名称查找
scene.getObjectByName('name');

相机

透视相机

const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// fov: 视野角度(度)
// aspect: 宽高比
// near: 近裁剪面
// far: 远裁剪面

camera.position.set(x, y, z);
camera.lookAt(x, y, z);
camera.lookAt(target.position);

正交相机

const camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);

相机方法

camera.position.set(x, y, z);
camera.lookAt(target);
camera.updateProjectionMatrix();

几何体

内置几何体

// 立方体
new THREE.BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments);

// 球体
new THREE.SphereGeometry(radius, widthSegments, heightSegments);

// 圆柱体
new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radialSegments);

// 圆锥体
new THREE.ConeGeometry(radius, height, radialSegments);

// 平面
new THREE.PlaneGeometry(width, height, widthSegments, heightSegments);

// 圆环
new THREE.TorusGeometry(radius, tube, radialSegments, tubularSegments);

// 圆环结
new THREE.TorusKnotGeometry(radius, tube, tubularSegments, radialSegments);

// 圆形
new THREE.CircleGeometry(radius, segments);

// 圆环
new THREE.RingGeometry(innerRadius, outerRadius, segments);

BufferGeometry

const geometry = new THREE.BufferGeometry();

// 设置顶点位置
const positions = new Float32Array([x1, y1, z1, x2, y2, z2, ...]);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));

// 设置法线
geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));

// 设置 UV
geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));

// 设置顶点颜色
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

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

几何体变换

geometry.translate(x, y, z);
geometry.rotateX(angle);
geometry.rotateY(angle);
geometry.rotateZ(angle);
geometry.scale(x, y, z);
geometry.center();

材质

基础材质

// 基础材质(不受光照影响)
new THREE.MeshBasicMaterial({
color: 0x00ff00,
wireframe: false,
transparent: false,
opacity: 1,
side: THREE.FrontSide // FrontSide | BackSide | DoubleSide
});

// 法线材质(调试用)
new THREE.MeshNormalMaterial();

受光照材质

// Lambert 材质(漫反射)
new THREE.MeshLambertMaterial({
color: 0x00ff00,
emissive: 0x000000
});

// Phong 材质(高光)
new THREE.MeshPhongMaterial({
color: 0x00ff00,
specular: 0xffffff,
shininess: 30
});

// 标准材质(PBR)
new THREE.MeshStandardMaterial({
color: 0x00ff00,
metalness: 0.5,
roughness: 0.5,
emissive: 0x000000,
envMapIntensity: 1
});

// 物理材质(高级 PBR)
new THREE.MeshPhysicalMaterial({
color: 0x00ff00,
metalness: 0,
roughness: 0.5,
clearcoat: 1,
clearcoatRoughness: 0,
transmission: 0,
thickness: 0.5,
ior: 1.5
});

特殊材质

// 卡通材质
new THREE.MeshToonMaterial({ color: 0x00ff00 });

// 点材质
new THREE.PointsMaterial({
color: 0x00ff00,
size: 0.1,
sizeAttenuation: true
});

// 精灵材质
new THREE.SpriteMaterial({
color: 0x00ff00,
transparent: true
});

// 着色器材质
new THREE.ShaderMaterial({
uniforms: { uTime: { value: 0 } },
vertexShader: '...',
fragmentShader: '...'
});

纹理贴图

const material = new THREE.MeshStandardMaterial({
map: diffuseTexture, // 漫反射贴图
normalMap: normalTexture, // 法线贴图
roughnessMap: roughnessTexture, // 粗糙度贴图
metalnessMap: metalnessTexture, // 金属度贴图
aoMap: aoTexture, // 环境光遮蔽贴图
emissiveMap: emissiveTexture, // 自发光贴图
alphaMap: alphaTexture // 透明度贴图
});

纹理

const loader = new THREE.TextureLoader();
const texture = loader.load('texture.jpg');

// 重复模式
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(x, y);

// 过滤
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;

// 各向异性过滤
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

// 偏移和旋转
texture.offset.set(x, y);
texture.rotation = angle;
texture.center.set(0.5, 0.5);

// 编码
texture.colorSpace = THREE.SRGBColorSpace;

网格

const mesh = new THREE.Mesh(geometry, material);

// 变换
mesh.position.set(x, y, z);
mesh.rotation.set(rx, ry, rz);
mesh.scale.set(sx, sy, sz);

// 欧拉角旋转顺序
mesh.rotation.order = 'XYZ';

// 四元数旋转
mesh.quaternion.setFromAxisAngle(axis, angle);

// 阴影
mesh.castShadow = true;
mesh.receiveShadow = true;

// 可见性
mesh.visible = true;

// 渲染顺序
mesh.renderOrder = 0;

// 用户数据
mesh.userData.customProperty = value;

光源

// 环境光
const ambient = new THREE.AmbientLight(color, intensity);

// 半球光
const hemi = new THREE.HemisphereLight(skyColor, groundColor, intensity);

// 方向光
const dir = new THREE.DirectionalLight(color, intensity);
dir.position.set(x, y, z);
dir.castShadow = true;
dir.shadow.mapSize.set(2048, 2048);
dir.shadow.camera.near = 0.5;
dir.shadow.camera.far = 50;

// 点光源
const point = new THREE.PointLight(color, intensity, distance, decay);
point.position.set(x, y, z);
point.castShadow = true;

// 聚光灯
const spot = new THREE.SpotLight(color, intensity, distance, angle, penumbra);
spot.position.set(x, y, z);
spot.target.position.set(x, y, z);
spot.castShadow = true;

// 矩形区域光
const rect = new THREE.RectAreaLight(color, intensity, width, height);

阴影

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

// 光源投射阴影
light.castShadow = true;
light.shadow.mapSize.set(2048, 2048);

// 方向光阴影范围
light.shadow.camera.left = -10;
light.shadow.camera.right = 10;
light.shadow.camera.top = 10;
light.shadow.camera.bottom = -10;
light.shadow.camera.near = 0.5;
light.shadow.camera.far = 50;

// 阴影偏移
light.shadow.bias = -0.0001;

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

动画

基础动画

const clock = new THREE.Clock();

function animate() {
requestAnimationFrame(animate);

const delta = clock.getDelta();
const elapsed = clock.getElapsedTime();

// 更新物体
mesh.rotation.y += delta;

renderer.render(scene, camera);
}

关键帧动画

const mixer = new THREE.AnimationMixer(object);

const positionKF = new THREE.VectorKeyframeTrack(
'.position',
[0, 1, 2],
[0, 0, 0, 2, 2, 0, 0, 0, 0]
);

const clip = new THREE.AnimationClip('action', duration, [positionKF]);
const action = mixer.clipAction(clip);
action.play();

// 更新混合器
mixer.update(delta);

动画控制

action.play();
action.pause();
action.stop();
action.reset();

action.timeScale = 1;
action.loop = THREE.LoopRepeat;
action.repetitions = Infinity;
action.weight = 1;

交互

射线检测

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

// 鼠标位置转归一化坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

// 设置射线
raycaster.setFromCamera(mouse, camera);

// 检测相交
const intersects = raycaster.intersectObjects(objects);
const intersects = raycaster.intersectObjects(objects, true); // 递归检测

// 相交结果
intersects[0].object; // 相交物体
intersects[0].point; // 相交点
intersects[0].distance; // 距离
intersects[0].face; // 相交面
intersects[0].uv; // UV 坐标

控制器

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.autoRotate = true;
controls.autoRotateSpeed = 2;
controls.minDistance = 2;
controls.maxDistance = 20;
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI / 2;
controls.enablePan = true;
controls.enableZoom = true;

// 更新
controls.update();

加载器

// GLTF 模型
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
new GLTFLoader().load('model.glb', (gltf) => {
scene.add(gltf.scene);
});

// 纹理
new THREE.TextureLoader().load('texture.jpg', (texture) => {});

// HDR 环境贴图
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
new RGBELoader().load('env.hdr', (texture) => {});

// OBJ 模型
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';

// FBX 模型
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';

// 字体
import { FontLoader } from 'three/addons/loaders/FontLoader.js';

后期处理

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

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

const bloomPass = new UnrealBloomPass(size, strength, radius, threshold);
composer.addPass(bloomPass);

// 渲染
composer.render();

数学工具

// 向量
const v = new THREE.Vector3(x, y, z);
v.set(x, y, z);
v.add(v2);
v.sub(v2);
v.multiplyScalar(s);
v.normalize();
v.length();
v.distanceTo(v2);
v.clone();

// 颜色
const c = new THREE.Color(0xff0000);
c.setHex(0xff0000);
c.setRGB(r, g, b);
c.setHSL(h, s, l);
c.lerp(c2, t);

// 欧拉角
const e = new THREE.Euler(x, y, z, 'XYZ');

// 四元数
const q = new THREE.Quaternion();
q.setFromAxisAngle(axis, angle);
q.setFromEuler(euler);

// 矩阵
const m = new THREE.Matrix4();
m.compose(position, quaternion, scale);
m.decompose(position, quaternion, scale);

// 射线
const ray = new THREE.Ray(origin, direction);
ray.intersectSphere(sphere, target);
ray.intersectPlane(plane, target);

// 平面
const plane = new THREE.Plane(normal, constant);

// 包围盒
const box = new THREE.Box3();
box.setFromObject(object);
box.containsPoint(point);
box.intersectsBox(box2);

常用常量

// 渲染面
THREE.FrontSide
THREE.BackSide
THREE.DoubleSide

// 混合模式
THREE.NormalBlending
THREE.AdditiveBlending
THREE.SubtractiveBlending
THREE.MultiplyBlending

// 纹理包裹
THREE.ClampToEdgeWrapping
THREE.RepeatWrapping
THREE.MirroredRepeatWrapping

// 过滤
THREE.NearestFilter
THREE.LinearFilter
THREE.NearestMipmapNearestFilter
THREE.NearestMipmapLinearFilter
THREE.LinearMipmapNearestFilter
THREE.LinearMipmapLinearFilter

// 阴影类型
THREE.BasicShadowMap
THREE.PCFShadowMap
THREE.PCFSoftShadowMap

// 颜色空间
THREE.SRGBColorSpace
THREE.LinearSRGBColorSpace

// 动画循环
THREE.LoopOnce
THREE.LoopRepeat
THREE.LoopPingPong

性能优化

// 实例化渲染
const mesh = new THREE.InstancedMesh(geometry, material, count);
mesh.setMatrixAt(index, matrix);
mesh.instanceMatrix.needsUpdate = true;

// 合并几何体
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
const merged = mergeGeometries([geo1, geo2]);

// 限制像素比
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

// 按需渲染
let needsRender = true;
controls.addEventListener('change', () => needsRender = true);

function animate() {
requestAnimationFrame(animate);
if (needsRender) {
renderer.render(scene, camera);
needsRender = false;
}
}

// 释放资源
geometry.dispose();
material.dispose();
texture.dispose();