mcpField
T
threejs

threejs

github.com/secondsky开发240467 次安装5 周前更新
代码仓库

关于 threejs

面向使用 Three.js 构建 3D Web 体验的开发者,提供 API 参考、实践建议和代码示例,涵盖渲染、动画及交互等主题。

主要功能

  1. 01场景、相机与渲染器设置
  2. 02几何体、材质与光照
  3. 03纹理、加载器与动画
  4. 04着色器与后期处理
  5. 05光线投射与相机控制
  6. 06性能优化与资源释放建议

适用场景

  1. 01搭建 Three.js 场景与渲染器
  2. 02加载并播放 GLTF 模型动画
  3. 03添加自定义着色器和视觉效果
  4. 04检查 3D 场景的性能实践

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/secondsky/claude-skills --skill threejs

安装后在 Claude Code 中输入/threejs确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
threejs-animation.md
threejs-fundamentals.md
threejs-geometry.md
threejs-interaction.md
threejs-lighting.md
threejs-loaders.md
threejs-materials.md
threejs-postprocessing.md
threejs-shaders.md
threejs-textures.md

详细介绍

Three.js 技能

概览

用于构建 Three.js 3D Web 体验的综合知识库。这项技能提供涵盖 Three.js 所有主要领域的准确 API 参考、最佳实践和可运行的代码示例。

Three.js 版本:r160+(2024 年 1 月)

快速参考

核心主题

这项技能涵盖 Three.js 的 10 个重要领域:

  1. 基础知识 - 场景设置、摄像机、渲染器、Object3D 层级结构
  2. 几何体 - 内置形状、BufferGeometry、自定义几何体、实例化
  3. 材质 - PBR 材质、着色器材质、材质属性
  4. 光照 - 光源类型、阴影、环境光照
  5. 纹理 - UV 映射、环境贴图、渲染目标
  6. 动画 - 关键帧动画、骨骼动画、动画混合
  7. 加载器 - GLTF/GLB 加载、异步模式、缓存
  8. 着色器 - GLSL 基础知识、ShaderMaterial、自定义效果
  9. 后期处理 - EffectComposer、泛光、景深、自定义处理通道
  10. 交互 - 射线投射、摄像机控制、鼠标/触摸输入

何时加载参考资料

根据当前任务加载详细参考文件:

  • 基本场景设置、摄像机、渲染器 → 加载 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