Comprehensive skill for Three.js 3D web development. Use this skill when building interactive 3D scenes, WebGL/WebGPU applications, product configurators, 3D visualizations, or immersive web experiences. Triggers on tasks involving Three.js, 3D rendering, scenes, cameras, meshes, materials, lights, animations, textures, or WebGL/WebGPU rendering.
Three.js WebGL/WebGPU 开发
概述
Three.js 是使用 WebGL 和 WebGPU 在网页浏览器中创建 3D 图形的行业标准 JavaScript 库。本技能提供构建高性能、交互式 3D 体验的全面指导,包括场景、相机、渲染器、几何体、材质、灯光、纹理和动画。
核心概念
场景图架构
Three.js 使用层次化的场景图,所有 3D 对象都组织在树状结构中:
Scene
├── Camera
├── Lights
│ ├── AmbientLight
│ ├── DirectionalLight
│ └── PointLight
├── Meshes
│ ├── Mesh (Geometry + Material)
│ └── InstancedMesh
└── Groups
基本组件
每个 Three.js 应用程序都需要以下核心元素:
- 场景:所有 3D 对象的容器
- 相机:定义观察视角
- 渲染器:将场景绘制到画布(WebGL 或 WebGPU)
- 几何体:定义对象的形状
- 材质:定义表面的外观
- 网格:结合几何体和材质
快速入门模式
基本场景设置
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 场景、相机、渲染器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
const camera = new THREE.PerspectiveCamera(
75, // 视野角度
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近裁剪面
1000 // 远裁剪面
);
camera.position.set(0, 2, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// 灯光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7.5);
directionalLight.castShadow = true;
scene.add(directionalLight);
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// 处理窗口大小变化
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
WebGPU 设置(现代替代方案)
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
renderer.toneMapping = THREE.LinearToneMapping;
renderer.toneMappingExposure = 1;
document.body.appendChild(renderer.domElement);
常见模式
1. 创建带材质的网格
// 基本网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.5
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 带纹理的网格
const loader = new THREE.TextureLoader();
const texture = loader.load('texture.jpg');
texture.colorSpace = THREE.SRGBColorSpace;
const texturedMaterial = new THREE.MeshStandardMaterial({
map: texture
});
const mesh = new THREE.Mesh(geometry, texturedMaterial);
scene.add(mesh);
2. 灯光策略
// 三点布光设置
function setupThreePointLight(scene) {
// 主光(主要)
const keyLight = new THREE.DirectionalLight(0xffffff, 3);
keyLight.position.set(5, 10, 7.5);
keyLight.castShadow = true;
scene.add(keyLight);
// 补光(柔化阴影)
const fillLight = new THREE.DirectionalLight(0xffffff, 1);
fillLight.position.set(-5, 5, -5);
scene.add(fillLight);
// 轮廓光(边缘定义)
const rimLight = new THREE.DirectionalLight(0xffffff, 0.5);
rimLight.position.set(0, 5, -10);
scene.add(rimLight);
// 环境光(基础照明)
const ambient = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambient);
}
// 物理光(真实)
const bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2);
bulbLight.power = 1700; // 流明(相当于100W灯泡)
bulbLight.castShadow = true;
scene.add(bulbLight);
// 半球光(天空 + 地面)
const hemiLight = new THREE.HemisphereLight(
0xddeeff, // 天空颜色
0x0f0e0d, // 地面颜色
0.02
);
scene.add(hemiLight);
3. 实例化几何体(性能优化)
// 用于高效渲染数千个相似对象
const geometry = new THREE.SphereGeometry(0.1, 16, 16);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);
const matrix = new THREE.Matrix4();
const color = new THREE.Color();
for (let i = 0; i < 1000; i++) {
matrix.setPosition(
Math.random() * 10 - 5,
Math.random() * 10 - 5,
Math.random() * 10 - 5
);
instancedMesh.setMatrixAt(i, matrix);
instancedMesh.setColorAt(i, color.setHex(Math.random() * 0xffffff));
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);
4. 加载 3D 模型(glTF)
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
// 设置加载器
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
// 加载模型
gltfLoader.load('model.glb', (gltf) => {
const model = gltf.scene;
// 启用阴影
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
scene.add(model);
// 处理动画
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
// 在动画循环中:
// mixer.update(deltaTime);
}
});
5. 阴影配置
// 在渲染器上启用阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 或 VSMShadowMap
// 配置灯光阴影
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.radius = 4;
directionalLight.shadow.blurSamples = 8;
// 投射/接收阴影的对象
mesh.castShadow = true;
mesh.receiveShadow = true;
6. 射线检测(交互)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(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, true);
if (intersects.length > 0) {
const object = intersects[0].object;
object.material.color.set(0xff0000);
}
}
window.addEventListener('click', onMouseClick);
集成模式
与 GSAP 动画集成
import gsap from 'gsap';
// 动画相机
gsap.to(camera.position, {
x: 5,
y: 3,
z: 10,
duration: 2,
ease: "power2.inOut",
onUpdate: () => {
camera.lookAt(scene.position);
}
});
// 动画网格属性
gsap.to(mesh.rotation, {
y: Math.PI * 2,
duration: 3,
repeat: -1,
ease: "none"
});
与 React 集成(参见 react-three-fiber 技能)
// Three.js 与 React Three Fiber 自然集成
// 使用 react-three-fiber 技能获取 React 集成模式
后处理
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(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // 强度
0.4, // 半径
0.85 // 阈值
);
composer.addPass(bloomPass);
// 在动画循环中:
composer.render();
性能优化
1. 几何体复用
// 不好:为每个网格创建新几何体
for (let i = 0; i < 100; i++) {
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
// 好:复用几何体
const sharedGeometry = new THREE.BoxGeometry(1, 1, 1);
for (let i = 0; i < 100; i++) {
const mesh = new THREE.Mesh(sharedGeometry, material);
scene.add(mesh);
}
2. 对重复对象使用 InstancedMesh
对于数百/数千个相同对象,使用 InstancedMesh(见上面的模式)。
3. 纹理优化
// 压缩纹理
texture.generateMipmaps = true;
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;
// 使用 2 的幂次方尺寸(512、1024、2048)
// 考虑使用纹理图集合并多个小纹理
4. 细节层次(LOD)
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0); // 0-50 单位
lod.addLevel(mediumDetailMesh, 50); // 50-100 单位
lod.addLevel(lowDetailMesh, 100); // 100+ 单位
scene.add(lod);
5. 视锥体剔除
Three.js 会自动剔除相机视野外的对象。确保对象具有正确的包围球:
mesh.geometry.computeBoundingSphere();
6. 释放资源
function disposeScene() {
scene.traverse((object) => {
if (object.geometry) object.geometry.dispose();
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(material => material.dispose());
} else {
object.material.dispose();
}
}
});
renderer.dispose();
}
最佳实践
1. 使用动画时钟确保时间一致
const clock = new THREE.Clock();
function animate() {
const deltaTime = clock.getDelta();
const elapsedTime = clock.getElapsedTime();
// 使用 deltaTime 实现帧无关动画
mesh.rotation.y += deltaTime * Math.PI * 0.5; // 每秒 90°
renderer.render(scene, camera);
}
2. 相机设置指南
- 视野角度:大多数应用为 45-75°
- 近平面:尽可能远(避免 z-fighting)
- 远平面:尽可能近(精度)
- 宽高比:始终匹配画布尺寸
3. 材质选择
- MeshBasicMaterial:无光照,纯色(调试、UI)
- MeshLambertMaterial:廉价的漫反射光照(移动端)
- MeshPhongMaterial:高光反射(旧标准)
- MeshStandardMaterial:PBR,真实(推荐)
- MeshPhysicalMaterial:高级 PBR(清漆、透射)
4. 坐标系
- Three.js 使用右手坐标系
- +Y 向上,+Z 朝向相机,+X 向右
- 旋转使用弧度(Math.PI = 180°)
5. 场景组织
// 分组相关对象
const building = new THREE.Group();
building.add(walls, roof, windows);
scene.add(building);
// 使用有意义的名称
mesh.name = 'player-character';
const found = scene.getObjectByName('player-character');
常见陷阱
1. 调整大小时未更新宽高比
窗口大小变化时,始终更新相机宽高比和投影矩阵。
2. 在动画循环中创建新对象
// 不好:内存泄漏
function animate() {
const geometry = new THREE.BoxGeometry(); // 每帧创建!
// ...
}
// 好:在循环外创建一次
const geometry = new THREE.BoxGeometry();
function animate() {
// 复用几何体
}
3. 忘记启用阴影
记住在渲染器、灯光和对象上启用阴影。
4. Z-fighting(闪烁)
- 增加近平面距离
- 减少远平面距离
- 避免重叠的共面表面
- 使用
material.polygonOffset = true和material.polygonOffsetFactor
5. 色彩空间问题
// 始终为纹理设置色彩空间
texture.colorSpace = THREE.SRGBColorSpace;
// 设置渲染器输出编码
renderer.outputColorSpace = THREE.SRGBColorSpace;
6. 未释放资源
不再需要时,始终调用 .dispose() 释放几何体、材质、纹理和渲染器。
资源
本技能包含捆绑资源以加速 Three.js 开发:
references/
api_reference.md:核心类(Scene、Camera、Renderer 等)的快速 API 参考materials_guide.md:全面的材质类型和属性optimization_checklist.md:性能优化策略
scripts/
setup_scene.py:生成 Three.js 场景设置的样板代码texture_optimizer.py:批量优化 Web 纹理(调整大小、压缩)gltf_validator.py:使用前验证 glTF 模型
assets/
starter_scene/:完整的 HTML/JS 样板项目shaders/:自定义 GLSL 着色器示例(顶点、片段)hdri/:用于 PBR 光照的环境贴图draco/:用于压缩模型的 DRACO 解码器
高级主题
自定义着色器(GLSL)
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uColor: { value: new THREE.Color(0x00ff00) }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float uTime;
uniform vec3 uColor;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(uColor * vUv.x, 1.0);
}
`
});
渲染目标(渲染到纹理)
const renderTarget = new THREE.WebGLRenderTarget(512, 512);
// 将场景渲染到纹理
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.setRenderTarget(null);
// 使用纹理
const material = new THREE.MeshBasicMaterial({
map: renderTarget.texture
});
GPU 计算(GPGPU)
使用 GPUComputationRenderer 进行粒子模拟、布料物理等。
何时使用此技能
在以下情况下使用此技能:
- 构建交互式 3D Web 体验
- 创建产品配置器或可视化工具
- 实现 WebGL/WebGPU 渲染
- 处理 3D 模型、场景或动画
- 优化 Three.js 性能
- 将 Three.js 与其他库集成(GSAP、React 等)
- 调试 Three.js 渲染问题
对于 React 集成,请使用 react-three-fiber 技能。
对于动画,请结合 gsap-scrolltrigger 技能。
对于 UI 动画,请使用 motion-framer 技能。






