前言
在之前的文章中有说过,我认为学习three.js最好的方法就是通过官网的例子去学习,在实践中练习。 因为Three.js中api很多,没有系统的学习文档,因此我觉得通过例子去学习,使用到某个概念或者api在深入学习它,这是一个不错的学习方式。 那么具体如何学习呢?

好了言归正传,下面我就教大家如何打出一套如来神掌,啊不对,是通过官方例子学习Three.js。

准备
阅读本文需要一点点的 WebGL 的知识点,至少这个文档的基础知识部分看完即可。
此外各位同学需要下载一下 Three.js 源码。源码里有我们需要用到的模型和官网例子的源码。
搭建一个 web 工程,本文演示创建基于 vue3 的工程。
bash
kotlin
npm init vue@latest
按照自己的喜好选择要安装哪些插件即可。生成的项目结构如下,我们为此项目添加 three.js,修改 package.json 如下。
json
json
{
"dependencies": {
...
"three": "^0.143.0"
}
}
如需使用 typescript,添加如下依赖:
json
perl
{
"devDependencies": {
...
"@types/three": "^0.143.0"
}
}
然后执行 npm install 安装依赖即可。
至此我们前期创建项目的准备工作就做完了。
体验例子
接下来我们在 three.js 官网上找到 感兴趣的例子,打开它的源码,先大致阅读一遍。通过这个例子中我们将学到如下几个知识点:相机、场景、网格、灯光、材质、形状、动画等。下面我们跟着例子写一遍代码,带大家学会如何通过例子去学习。

写(抄)代码
源码在上面的链接已经给出来,我就不在这里凑字数了。我在这里一步一步地写一下上述例子的代码,并演示如何通过此例来学习 Three.js。那么就让我们愉快地开始吧。
首先,我们在 Vue 项目中创建一个组件(比如 ThreeExample.vue),并在 mounted 生命周期中编写 Three.js 代码。当然,你也可以用原生 HTML+JS,但这里我们以 Vue3 为例。
1. 初始化场景、相机和渲染器
打开官方例子的源码,我们会看到一开始就创建了 scene、camera、renderer 这三个基本对象。这是每一个 Three.js 应用的起点。
js
dart
// 1. 创建场景
const scene = new THREE.Scene();
// 2. 创建透视相机(参数:视野角度、宽高比、近裁面、远裁面)
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.set(2, 2, 5); // 设置相机位置
camera.lookAt(0, 0, 0); // 让相机看向原点
// 3. 创建 WebGL 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement); // 将 canvas 添加到页面
学习点 :这里我们用到了 PerspectiveCamera,如果不清楚它的参数含义,就可以去 官网文档 查看。文档会详细解释每个参数的作用,比如 fov(视野角度)决定了你能看到多大的范围,aspect(宽高比)通常设为画布的宽度/高度,否则图像会被拉伸。
2. 添加光源
例子中使用了多种光源:环境光、点光源和聚光灯。光源是让物体可见并产生阴影和立体感的关键。
js
csharp
// 环境光:提供基础照明,均匀照亮所有面
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
// 点光源:从某个点向所有方向发射光线
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(1, 2, 3);
scene.add(pointLight);
// 还可以添加其他光源,比如聚光灯、平行光等,根据需要选择
学习点 :看到 AmbientLight 和 PointLight,我们可以去文档了解每种光源的特点和适用场景。例如环境光没有方向,通常用来提亮阴影部分;点光源类似灯泡,会产生阴影(需配合阴影设置)。
3. 加载模型
例子中加载了一个带有骨骼动画和变形动画的模型(models/gltf/Soldier.glb)。我们需要使用 GLTFLoader 来加载 glTF 格式的模型。
首先,引入加载器(需要额外导入):
js
javascript
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
然后,在代码中创建加载器并加载模型:
js
ini
const loader = new GLTFLoader();
loader.load('models/gltf/Soldier.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
// 获取模型的动画剪辑
const animations = gltf.animations;
if (animations && animations.length) {
// 创建动画混合器并播放动画(后面会讲到)
mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(animations[0]);
action.play();
}
}, undefined, (error) => {
console.error('模型加载失败:', error);
});
注意:模型路径需要根据你存放的位置调整。官方源码中的模型路径是相对 examples/ 目录的,你可以把 models 文件夹复制到你的 public 目录下。
学习点 :遇到 GLTFLoader,我们可以去文档或源码中查看它的用法。GLTF 是 Three.js 推荐的 3D 模型格式,支持动画、材质、骨骼等。通过这个例子,我们学会了如何加载外部模型并添加到场景。
4. 动画循环
例子中使用 requestAnimationFrame 实现动画循环,并在每一帧更新动画混合器。
js
scss
let mixer = null; // 在加载模型时赋值
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta(); // 获取时间差,用于平滑动画
if (mixer) {
mixer.update(delta); // 更新动画混合器
}
// 渲染场景
renderer.render(scene, camera);
}
// 创建 Clock 对象用于计算时间差
const clock = new THREE.Clock();
// 启动动画循环
animate();
学习点 :AnimationMixer 和 Clock 是 Three.js 中处理动画的重要 API。通过查看文档,我们可以了解 mixer.update(delta) 如何基于时间差驱动模型动画。
5. 处理窗口大小变化
为了让画布自适应窗口,我们需要监听窗口的 resize 事件,更新相机宽高比和渲染器尺寸。
js
javascript
window.addEventListener('resize', onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix(); // 必须调用,使相机参数生效
renderer.setSize(window.innerWidth, window.innerHeight);
}
学习点 :updateProjectionMatrix() 方法的作用是更新相机的投影矩阵,当相机参数改变时(如宽高比),需要调用此方法让 Three.js 重新计算投影。
6. 遇到不懂的就去官网查找 API
在"抄"代码的过程中,你一定会遇到很多陌生的 API,比如 PointLight、GLTFLoader、AnimationMixer、Clock、updateProjectionMatrix 等等。这时候,最好的学习方式就是打开 Three.js 官方文档,搜索这些 API,仔细阅读其用法和参数含义。
例如,你看到 camera.lookAt(0, 0, 0),可以查阅 Object3D 的 lookAt 方法,理解它是如何让物体朝向某个点的。
文档通常包含详细说明和示例代码,非常有助于深入理解。通过这样的方式,你不仅学会了这个例子,还能举一反三,应用到其他场景。
完整的代码整合
将上述代码片段整合到一个 Vue 组件中,大概如下(省略了样式和模板部分):
vue
ini
<template>
<div ref="container" style="width:100%; height:100vh;"></div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const container = ref(null);
onMounted(() => {
// 1. 创建场景
const scene = new THREE.Scene();
// 2. 创建相机
const camera = new THREE.PerspectiveCamera(45, container.value.clientWidth / container.value.clientHeight, 1, 2000);
camera.position.set(2, 2, 5);
camera.lookAt(0, 0, 0);
// 3. 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.value.clientWidth, container.value.clientHeight);
renderer.setPixelRatio(window.devicePixelRatio);
container.value.appendChild(renderer.domElement);
// 4. 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(1, 2, 3);
scene.add(pointLight);
// 5. 加载模型
const loader = new GLTFLoader();
let mixer = null;
loader.load('/models/gltf/Soldier.glb', (gltf) => {
const model = gltf.scene;
scene.add(model);
if (gltf.animations.length) {
mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
}
});
// 6. 动画循环
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if (mixer) {
mixer.update(delta);
}
renderer.render(scene, camera);
}
animate();
// 7. 窗口大小自适应
const onWindowResize = () => {
camera.aspect = container.value.clientWidth / container.value.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.value.clientWidth, container.value.clientHeight);
};
window.addEventListener('resize', onWindowResize);
});
</script>
如何进一步深入学习
通过"抄"这个例子,我们已经接触到了 Three.js 的核心概念。但这仅仅是开始。下面是一些进阶学习建议:
- 修改参数,观察效果:尝试调整相机位置、光源颜色和强度、模型缩放比例等,实时查看变化,加深理解。
- 尝试添加交互 :比如使用
OrbitControls让用户可以用鼠标旋转视角。引入对应的控制器并启用,能极大地提升体验。 - 研究动画:例子中只播放了第一个动画剪辑。你可以尝试播放其他动画,或者混合多个动画。
- 阅读更多例子:Three.js 官网有上百个例子,涵盖了粒子系统、后期处理、物理效果等。按照同样的方法,一个个"抄"过去,你的 Three.js 水平会飞速提升。
- 参与社区:遇到问题时,可以到 Stack Overflow、GitHub Issues 或中文社区(如掘金)提问,也可以阅读他人的源码和文章。
最后
以上就是本文的全部内容了。我们通过一个官方的骨骼动画例子,逐步学习了 Three.js 的基础:场景、相机、渲染器、光源、模型加载、动画和自适应窗口。更重要的是,我们实践了"通过例子学习"的方法------遇到不懂的 API 就去查文档,然后亲手写一遍。
这种学习方法不仅适用于 Three.js,也适用于其他任何技术栈。希望各位同学能举一反三,不再畏惧陌生的框架和库,勇敢地打开源码,开始你的"抄"级学习之旅!
如果感觉阅读本文后有所收获,欢迎点赞、收藏和评论,也欢迎分享你通过例子学习 Three.js 的心得。我们下期再见!
注意:本文代码基于 Three.js r143 版本,不同版本 API 可能略有差异,请以你实际使用的版本为准。模型文件需自行从 Three.js 源码仓库中获取,并放置于项目的公共目录下。