mcpField
T
threejs-webgl

threejs-webgl

github.com/freshtechbro开发1.2k4.6k 次安装49 周前更新
代码仓库

关于 threejs-webgl

threejs-webgl 是面向 Three.js 开发者的技能指南,涵盖浏览器中的 WebGL 与 WebGPU 交互式 3D 场景构建,并提供优化建议、参考资料和辅助脚本。

主要功能

  1. 01场景、相机与渲染器配置
  2. 02几何体、材质和灯光实践
  3. 03模型加载与交互处理
  4. 04动画与后期处理示例
  5. 05性能优化和资源清理
  6. 06参考资料及辅助脚本

适用场景

  1. 01搭建交互式 3D 网页体验
  2. 02加载和展示 glTF 模型
  3. 03优化 Three.js 场景性能

安装

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

$npx skills add https://github.com/freshtechbro/claudedesignskills --skill threejs-webgl

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

包含的文件

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

assets/starter_scene/
index.html
main.js
README.md
references/
api_reference.md
materials_guide.md
optimization_checklist.md
scripts/
setup_scene.py

详细介绍

Three.js WebGL/WebGPU 开发

概述

Three.js 是业界标准的 JavaScript 库,可在 Web 浏览器中使用 WebGL 和 WebGPU 创建 3D 图形。本技能为构建高性能、可交互的 3D 体验提供全面指导,涵盖场景、相机、渲染器、几何体、材质、光源、纹理和动画。

核心概念

场景图架构

Three.js 使用分层场景图,将所有 3D 对象组织成树形结构:

Scene
├── Camera
├── Lights
│   ├── AmbientLight
│   ├── DirectionalLight
│   └── PointLight
├── Meshes
│   ├── Mesh (Geometry + Material)
│   └── InstancedMesh
└── Groups
必备组件

每个 Three.js 应用都需要以下核心元素:

  1. 场景:容纳所有 3D 对象
  2. 相机:定义观察视角
  3. 渲染器:将场景绘制到画布上(WebGL 或 WebGPU)
  4. 几何体:定义对象的形状
  5. 材质:定义表面外观
  6. 网格:组合几何体和材质

快速入门模式

基本场景设置
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// Scene, Camera, Renderer
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);

const camera = new THREE.PerspectiveCamera(
  75, // FOV
  window.innerWidth / window.innerHeight, // Aspect ratio
  0.1, // Near clipping plane
  1000 // Far clipping plane
);
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);

// Lighting
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);

// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;

// Animation Loop
function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);
}

animate();

// Handle Resize
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. 使用材质创建网格
// Basic Mesh
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);

// Textured Mesh
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. 光照策略
// Three-Point Lighting Setup
function setupThreePointLight(scene) {
  // Key Light (Main)
  const keyLight = new THREE.DirectionalLight(0xffffff, 3);
  keyLight.position.set(5, 10, 7.5);
  keyLight.castShadow = true;
  scene.add(keyLight);

  // Fill Light (Softens shadows)
  const fillLight = new THREE.DirectionalLight(0xffffff, 1);
  fillLight.position.set(-5, 5, -5);
  scene.add(fillLight);

  // Rim Light (Edge definition)
  const rimLight = new THREE.DirectionalLight(0xffffff, 0.5);
  rimLight.position.set(0, 5, -10);
  scene.add(rimLight);

  // Ambient (Base illumination)
  const ambient = new THREE.AmbientLight(0x404040, 0.5);
  scene.add(ambient);
}

// Physical Light (Realistic)
const bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2);
bulbLight.power = 1700; // Lumens (100W bulb equivalent)
bulbLight.castShadow = true;
scene.add(bulbLight);

// Hemisphere Light (Sky + Ground)
const hemiLight = new THREE.HemisphereLight(
  0xddeeff, // Sky color
  0x0f0e0d, // Ground color
  0.02
);
scene.add(hemiLight);
3. 实例化几何体(性能优化)
// For rendering thousands of similar objects efficiently
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';

// Setup loaders
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');

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;
    }
  });

  scene.add(model);

  // Handle animations
  if (gltf.animations.length > 0) {
    const mixer = new THREE.AnimationMixer(model);
    const action = mixer.clipAction(gltf.animations[0]);
    action.play();

    // In animation loop:
    // mixer.update(deltaTime);
  }
});
5. 阴影配置
// Enable shadows on renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // or VSMShadowMap

// Configure light shadows
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;

// Objects casting/receiving shadows
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';

// Animate camera
gsap.to(camera.position, {
  x: 5,
  y: 3,
  z: 10,
  duration: 2,
  ease: "power2.inOut",
  onUpdate: () => {
    camera.lookAt(scene.position);
  }
});

// Animate mesh properties
gsap.to(mesh.rotation, {
  y: Math.PI * 2,
  duration: 3,
  repeat: -1,
  ease: "none"
});
与 React 集成(参见 react-three-fiber skill)
// Three.js integrates naturally with React Three Fiber
// Use the react-three-fiber skill for React integration patterns
使用后期处理
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, // strength
  0.4, // radius
  0.85 // threshold
);
composer.addPass(bloomPass);

// In animation loop:
composer.render();

性能优化

1. 复用几何体
// Bad: Creates new geometry for each mesh
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);
}

// Good: Reuse geometry
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. 纹理优化
// Compress textures
texture.generateMipmaps = true;
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;

// Use power-of-two dimensions (512, 1024, 2048)
// Consider texture atlases for multiple small textures
4. 细节层次(LOD)
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0);    // 0-50 units
lod.addLevel(mediumDetailMesh, 50);  // 50-100 units
lod.addLevel(lowDetailMesh, 100);    // 100+ units
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();

  // Use deltaTime for frame-independent animations
  mesh.rotation.y += deltaTime * Math.PI * 0.5; // 90° per second

  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. 场景组织
// Group related objects
const building = new THREE.Group();
building.add(walls, roof, windows);
scene.add(building);

// Use meaningful names
mesh.name = 'player-character';
const found = scene.getObjectByName('player-character');

常见问题

1. 调整窗口大小时未更新纵横比

窗口大小变化时,始终更新相机纵横比和投影矩阵。

2. 在动画循环中创建新对象
// Bad: Memory leak
function animate() {
  const geometry = new THREE.BoxGeometry(); // Created every frame!
  // ...
}

// Good: Create once outside loop
const geometry = new THREE.BoxGeometry();
function animate() {
  // Reuse geometry
}
3. 忘记启用阴影

记得在渲染器、光源和对象上启用阴影。

4. Z-fighting(闪烁)
  • 增大近裁剪面距离
  • 减小远裁剪面距离
  • 避免重叠的共面表面
  • 使用 material.polygonOffset = true 和 material.polygonOffsetFactor
5. 色彩空间问题
// Always set color space for textures
texture.colorSpace = THREE.SRGBColorSpace;

// Set renderer output encoding
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);

// Render scene to texture
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.setRenderTarget(null);

// Use texture
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 技能。

评论

支持 Markdown