1. 初探3D世界的钥匙
第一次打开three.js的官方示例页面时,那些在网页里自由旋转的3D模型让我想起了小时候玩的万花筒。作为前端开发者,我们习惯了在二维平面上布局元素,而three.js就像突然打开的第三维度,让网页拥有了深度和空间感。这个轻量级的JavaScript 3D库,用WebGL的力量将OpenGL ES 2.0带进了浏览器,不需要任何插件就能创建令人惊艳的3D场景。
我最初接触three.js是为了给电商网站做商品3D展示。传统的主图轮播根本无法展示家具的立体细节,而three.js只需要不到100KB的库文件,就能让用户在网页里360°查看产品。更令人惊喜的是,它的学习曲线比直接使用WebGL平缓得多——如果你熟悉基本的JavaScript和DOM操作,跟着官方文档的示例代码,半小时内就能让第一个立方体在网页里转起来。
2. 核心概念拆解
2.1 场景图的树形结构
three.js的整个体系建立在场景图(scene graph)这个概念上,就像导演在布置一个3D舞台。首先需要创建场景(Scene)作为容器,这个场景对象会维护一个树形结构:场景是根节点,下面可以添加各种3D对象(Mesh)、灯光(Light)和相机(Camera)。我特别喜欢这种层级关系的设计,比如你可以创建一个汽车模型作为父对象,然后把四个车轮作为子对象添加进去,移动汽车时车轮会自动跟随。
const scene = new THREE.Scene(); const car = new THREE.Group(); const wheel1 = new THREE.Mesh(wheelGeometry, wheelMaterial); car.add(wheel1); scene.add(car);2.2 渲染器的魔法
WebGLRenderer是three.js的核心魔法师,它负责把3D场景转换成浏览器能显示的2D图像。创建渲染器时需要特别注意抗锯齿(antialias)参数:
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true // 允许透明背景 }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);我在项目中遇到过性能问题:在低配设备上开启抗锯齿会导致帧率骤降。解决方案是动态检测设备能力,为移动端关闭抗锯齿:
const isMobile = /Mobi|Android/i.test(navigator.userAgent); const renderer = new THREE.WebGLRenderer({ antialias: !isMobile });2.3 相机的视角控制
Three.js提供两种主要相机:正交相机(OrthographicCamera)和透视相机(PerspectiveCamera)。做产品展示时我推荐透视相机,因为它模拟人眼视角,会产生近大远小的效果:
const camera = new THREE.PerspectiveCamera( 75, // 视野角度(FOV) window.innerWidth / window.innerHeight, // 宽高比 0.1, // 近裁剪面 1000 // 远裁剪面 ); camera.position.z = 5;调试相机参数有个技巧:先用OrbitControls添加旋转控制,然后边拖动边调整FOV和位置,直到获得最佳视角。
3. 创建你的第一个3D对象
3.1 几何体与材质
立方体是最简单的入门对象,但即使是这个基础形状,three.js也提供了丰富的定制选项:
const geometry = new THREE.BoxGeometry(1, 1, 1, 10, 10, 10); // 最后三个参数控制每个面的细分段数 const material = new THREE.MeshStandardMaterial({ color: 0x3498db, roughness: 0.4, metalness: 0.7 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube);这里我推荐使用MeshStandardMaterial而不是基础的MeshBasicMaterial,因为它支持PBR(基于物理的渲染),配合灯光能产生更真实的质感。
3.2 动画循环的实现
让立方体旋转起来需要用到requestAnimationFrame:
function animate() { requestAnimationFrame(animate); cube.rotation.x += 0.01; cube.rotation.y += 0.01; renderer.render(scene, camera); } animate();实际项目中要注意以下几点:
- 在组件卸载时一定要取消动画循环
- 复杂的动画建议使用gsap等专业库
- 记得添加stats.js监控帧率
4. 灯光与阴影的艺术
4.1 光源类型选择
Three.js提供多种光源,我最常用的是这些组合:
- DirectionalLight 模拟太阳光,产生清晰阴影
- HemisphereLight 模拟天光,提供环境照明
- PointLight 模拟灯泡,用于局部补光
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(5, 5, 5); directionalLight.castShadow = true; scene.add(directionalLight); // 柔和的环境光 const ambientLight = new THREE.AmbientLight(0x404040); scene.add(ambientLight);4.2 阴影优化技巧
开启阴影是性能杀手,需要精细控制:
directionalLight.shadow.mapSize.width = 1024; // 提高阴影质量 directionalLight.shadow.mapSize.height = 1024; directionalLight.shadow.camera.near = 0.5; // 调整阴影相机范围 directionalLight.shadow.camera.far = 20; renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 柔和阴影在移动设备上,可以考虑用假阴影贴图替代实时阴影,或者完全关闭阴影。
5. 模型加载实战
5.1 GLTF加载流程
GLTF是three.js推荐的3D模型格式,加载需要GLTFLoader:
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; const loader = new GLTFLoader(); loader.load( 'model.glb', (gltf) => { const model = gltf.scene; model.scale.set(0.1, 0.1, 0.1); scene.add(model); }, undefined, (error) => { console.error('加载模型出错:', error); } );5.2 性能优化要点
- 使用Draco压缩减少模型体积
- 对静态模型合并几何体(merge geometries)
- 实现加载进度条提升用户体验
- 添加错误边界处理加载失败情况
6. 常见问题排雷指南
6.1 对象不可见排查清单
- 检查相机位置和朝向
- 确认对象在相机视锥体内
- 验证材质是否透明度过高
- 查看控制台是否有着色器编译错误
6.2 内存泄漏预防
Three.js对象需要手动释放内存:
// 清理几何体 geometry.dispose(); // 清理材质 material.dispose(); // 移除纹理 texture.dispose();在SPA应用中尤其要注意,页面切换时应该清理所有three.js资源。
7. 项目结构最佳实践
经过多个项目迭代,我总结出这样的目录结构:
/src /assets /models /textures /components SceneManager.js CameraController.js /utils loaders.js helpers.js index.js关键点是将three.js相关代码与业务逻辑分离,SceneManager负责初始化场景、渲染循环,CameraController处理视角变换,loaders集中管理模型加载。
8. 性能监控与优化
8.1 使用stats.js
import Stats from 'stats.js'; const stats = new Stats(); stats.showPanel(0); // 0: fps, 1: ms, 2: mb document.body.appendChild(stats.dom); function animate() { stats.begin(); // 渲染代码... stats.end(); }8.2 渲染优化技巧
- 减少实时阴影数量
- 使用LOD(Level of Detail)系统
- 实现视锥体剔除(frustum culling)
- 对静态物体使用合并实例(instanced mesh)
9. 进阶学习路线
掌握基础后,可以逐步探索:
- 着色器编程(ShaderMaterial)
- 后期处理(EffectComposer)
- 物理引擎(Cannon.js)
- VR/AR集成
我个人的学习方法是:先修改官方示例代码,理解每个参数的作用;然后尝试复现Dribbble上的3D动效;最后挑战完整的3D网页项目。three.js的文档虽然全面,但最好的学习资源其实是它的示例库——200多个覆盖各种场景的实例,每个都是最佳实践。