关于 threejs-webgl
threejs-webgl 是面向 Three.js 开发者的技能指南,涵盖浏览器中的 WebGL 与 WebGPU 交互式 3D 场景构建,并提供优化建议、参考资料和辅助脚本。
主要功能
- 01场景、相机与渲染器配置
- 02几何体、材质和灯光实践
- 03模型加载与交互处理
- 04动画与后期处理示例
- 05性能优化和资源清理
- 06参考资料及辅助脚本
适用场景
- 01搭建交互式 3D 网页体验
- 02加载和展示 glTF 模型
- 03优化 Three.js 场景性能
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ freshtechbro/ claudedesignskills --skill threejs-webgl安装后在 Claude Code 中输入/threejs-webgl确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/starter_scene/index.htmlmain.jsREADME.mdreferences/api_reference.mdmaterials_guide.mdoptimization_checklist.mdscripts/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 应用都需要以下核心元素:
- 场景:容纳所有 3D 对象
- 相机:定义观察视角
- 渲染器:将场景绘制到画布上(WebGL 或 WebGPU)
- 几何体:定义对象的形状
- 材质:定义表面外观
- 网格:组合几何体和材质
快速入门模式
基本场景设置
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