关于 threejs
面向使用 Three.js 构建 3D Web 体验的开发者,提供 API 参考、实践建议和代码示例,涵盖渲染、动画及交互等主题。
主要功能
- 01场景、相机与渲染器设置
- 02几何体、材质与光照
- 03纹理、加载器与动画
- 04着色器与后期处理
- 05光线投射与相机控制
- 06性能优化与资源释放建议
适用场景
- 01搭建 Three.js 场景与渲染器
- 02加载并播放 GLTF 模型动画
- 03添加自定义着色器和视觉效果
- 04检查 3D 场景的性能实践
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill threejs安装后在 Claude Code 中输入/threejs确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/threejs-animation.mdthreejs-fundamentals.mdthreejs-geometry.mdthreejs-interaction.mdthreejs-lighting.mdthreejs-loaders.mdthreejs-materials.mdthreejs-postprocessing.mdthreejs-shaders.mdthreejs-textures.md详细介绍
Three.js 技能
概览
用于构建 Three.js 3D Web 体验的综合知识库。这项技能提供涵盖 Three.js 所有主要领域的准确 API 参考、最佳实践和可运行的代码示例。
Three.js 版本:r160+(2024 年 1 月)
快速参考
核心主题
这项技能涵盖 Three.js 的 10 个重要领域:
- 基础知识 - 场景设置、摄像机、渲染器、Object3D 层级结构
- 几何体 - 内置形状、BufferGeometry、自定义几何体、实例化
- 材质 - PBR 材质、着色器材质、材质属性
- 光照 - 光源类型、阴影、环境光照
- 纹理 - UV 映射、环境贴图、渲染目标
- 动画 - 关键帧动画、骨骼动画、动画混合
- 加载器 - GLTF/GLB 加载、异步模式、缓存
- 着色器 - GLSL 基础知识、ShaderMaterial、自定义效果
- 后期处理 - EffectComposer、泛光、景深、自定义处理通道
- 交互 - 射线投射、摄像机控制、鼠标/触摸输入
何时加载参考资料
根据当前任务加载详细参考文件:
- 基本场景设置、摄像机、渲染器 → 加载
references/threejs-fundamentals.md - 创建形状、自定义几何体、实例化 → 加载
references/threejs-geometry.md - 材质属性、PBR、着色器材质 → 加载
references/threejs-materials.md - 添加光源、配置阴影 → 加载
references/threejs-lighting.md - 纹理加载、UV 映射、环境贴图 → 加载
references/threejs-textures.md - 对象动画、GLTF 动画、混合 → 加载
references/threejs-animation.md - 加载 GLTF/GLB 模型、Draco 压缩 → 加载
references/threejs-loaders.md - 编写 GLSL 着色器、自定义视觉效果 → 加载
references/threejs-shaders.md - 添加泛光、景深、屏幕效果 → 加载
references/threejs-postprocessing.md - 射线投射、鼠标拾取、摄像机控制 → 加载
references/threejs-interaction.md
快速入门示例
1. 基础知识:基本场景
import * as THREE from 'three';
// Scene, camera, renderer
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.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// Create cube
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Add light
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);
camera.position.z = 5;
// Animation loop
function animate() {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
// Responsive
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
2. 几何体:创建形状
// Built-in geometries
const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(0.5, 32, 32);
const plane = new THREE.PlaneGeometry(10, 10);
// Custom BufferGeometry
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1, 0, // vertex 0
1, -1, 0, // vertex 1
1, 1, 0, // vertex 2
-1, 1, 0 // vertex 3
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
// Indices for triangles
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// Instancing for many copies
const count = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20
);
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(instancedMesh);
3. 材质:PBR 材质
// Standard PBR material
const material = new THREE.MeshStandardMaterial({
color: 0xffffff,
metalness: 0.5,
roughness: 0.5,
map: colorTexture,
normalMap: normalTexture,
roughnessMap: roughnessTexture,
metalnessMap: metalnessTexture,
envMap: environmentMap,
envMapIntensity: 1
});
// Physical material (advanced PBR)
const glassMaterial = new THREE.MeshPhysicalMaterial({
color: 0xffffff,
metalness: 0,
roughness: 0,
transmission: 1, // Glass transparency
thickness: 0.5,
ior: 1.5, // Index of refraction
envMapIntensity: 1
});
// Shader material (custom)
const shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color: { value: new THREE.Color(0xff0000) }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float time;
uniform vec3 color;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(color * sin(vUv.x * 10.0 + time), 1.0);
}
`
});
4. 光照:基本光照
// Ambient light (uniform everywhere)
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);
// Directional light (sun)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 5);
dirLight.castShadow = true;
// Shadow configuration
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
scene.add(dirLight);
// Point light (bulb)
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);
// Enable shadows on renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
// Enable on objects
mesh.castShadow = true;
mesh.receiveShadow = true;
5. 纹理:加载纹理
const loader = new THREE.TextureLoader();
// Load color texture
const colorTexture = loader.load('texture.jpg');
colorTexture.colorSpace = THREE.SRGBColorSpace; // Important for color accuracy
// Configure texture
colorTexture.wrapS = THREE.RepeatWrapping;
colorTexture.wrapT = THREE.RepeatWrapping;
colorTexture.repeat.set(4, 4);
// HDR environment map
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';
const rgbeLoader = new RGBELoader();
rgbeLoader.load('environment.hdr', (texture) => {
texture.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = texture;
scene.background = texture;
});
// Cube texture (skybox)
const cubeLoader = new THREE.CubeTextureLoader();
const cubeTexture = cubeLoader.load([
'px.jpg', 'nx.jpg', // +X, -X
'py.jpg', 'ny.jpg', // +Y, -Y
'pz.jpg', 'nz.jpg' // +Z, -Z
]);
scene.background = cubeTexture;
6. 动画:简单动画
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('model.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
// Create animation mixer
const mixer = new THREE.AnimationMixer(model);
// Play all animations
gltf.animations.forEach((clip) => {
const action = mixer.clipAction(clip);
action.play();
});
// Update in animation loop
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta);
renderer.render(scene, camera);
}
animate();
});
// Procedural animation
function animate() {
const time = clock.getElapsedTime();
mesh.rotation.y = time;
mesh.position.y = Math.sin(time) * 0.5;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
7. 加载器:加载 GLTF 模型
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
// Setup Draco compression support
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
// Load model
gltfLoader.load('model.glb', (gltf) => {
const model = gltf.scene;
// Enable shadows
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
// Center and scale
const box = new THREE.Box3().setFromObject(model);
const center = box.getCenter(new THREE.Vector3());
model.position.sub(center);
scene.add(model);
});
// Async/Promise pattern
async function loadModel(url) {
return new Promise((resolve, reject) => {
gltfLoader.load(url, resolve, undefined, reject);
});
}
const gltf = await loadModel('model.glb');
scene.add(gltf.scene);
8. 着色器:自定义着色器材质
const material = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
amplitude: { value: 0.5 }
},
vertexShader: `
uniform float time;
uniform float amplitude;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 pos = position;
// Wave displacement
pos.z += sin(pos.x * 5.0 + time) * amplitude;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
uniform float time;
varying vec2 vUv;
void main() {
vec3 color = vec3(vUv, 0.5 + 0.5 * sin(time));
gl_FragColor = vec4(color, 1.0);
}
`
});
// Update in animation loop
function animate() {
material.uniforms.time.value = clock.getElapsedTime();
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
9. 后期处理:添加泛光
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
// Create composer
const composer = new EffectComposer(renderer);
// Render scene pass
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// Bloom pass
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold
);
composer.addPass(bloomPass);
// Use composer instead of renderer
function animate() {
requestAnimationFrame(animate);
composer.render(); // NOT renderer.render()
}
// Handle resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
composer.setSize(window.innerWidth, window.innerHeight);
});
10. 交互:射线投射
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// Camera controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// Raycasting setup
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// Convert mouse to normalized coordinates
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Raycast from camera
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const object = intersects[0].object;
console.log('Clicked:', object);
console.log('Point:', intersects[0].point);
// Highlight selected object
object.material.emissive.set(0x444444);
}
}
window.addEventListener('click', onMouseClick);
// Update controls in animation loop
function animate() {
requestAnimationFrame(animate);
controls.update(); // Required if enableDamping is true
renderer.render(scene, camera);
}
常见模式
正确释放资源
// Dispose geometries, materials, textures
geometry.dispose();
material.dispose();
texture.dispose();
// Remove from scene
scene.remove(mesh);
// Dispose renderer
renderer.dispose();
响应式渲染
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
性能优化
- 对重复对象使用实例化(
InstancedMesh) - 启用视锥体剔除(默认已启用)
- 释放不再使用的资源
- 为复杂场景使用适当的 LOD(细节层次)
- 合并几何体以减少绘制调用
- 限制启用的光源数量(每个光源都会增加着色器复杂度)
- 使用纹理图集以减少纹理切换
版本信息
Three.js 版本:r160+(2024 年 1 月)
导入格式:ES6 模块(three、three/addons/*)
已验证:2024-01
另请参阅
评论
支持 Markdown