跳转到主要内容

Three.js 入门:从零搭建 3D 项目架构

从零搭建 Vite + TypeScript + Three.js 项目,理解 Scene / Camera / Renderer 三件套,掌握 requestAnimationFrame + delta time 动画循环。

Three.js 的一切始于三个对象:Scene(舞台)、Camera(镜头)、Renderer(投影仪)。掌握这三件套,就掌握了 3D 世界的入场券。

01 学习目标

本节课从零搭建一个完整的 Three.js 项目,目标是:

  • 搭建 Vite + TypeScript + Three.js 项目脚手架
  • 理解 Scene / Camera / Renderer 三件套的职责
  • 掌握 requestAnimationFrame + delta time 动画循环
  • 学会窗口自适应和像素比设置

02 Three.js 三件套

Three.js 的一切始于三个对象:

对象类比职责
Scene舞台放置所有物体、灯光、相机的容器
Camera摄像机决定观众看到什么(位置、角度、范围)
Renderer投影仪把 3D 场景计算成 2D 像素画到 canvas 上
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, width/height, 0.1, 1000)
const renderer = new THREE.WebGLRenderer({ canvas })

03 动画循环 + Delta Time

为什么需要 delta time?

不同设备帧率不同(60fps vs 144fps)。如果直接写 rotation += 0.01,144fps 的设备转速是 60fps 的 2.4 倍。

解决方案:乘以 delta(上一帧到这一帧经过的秒数),保证速度与时间挂钩,与帧率无关。

// 错误:帧率相关
cube.rotation.x += 0.01

// 正确:时间相关
cube.rotation.x += speed * delta  // delta 单位是秒

requestAnimationFrame 的时间戳

  • 参数 now 是毫秒级时间戳(performance.now() 的值)
  • delta = (now - lastTime) / 1000 转成秒

04 模块化设计:SceneManager

把 Scene/Camera/Renderer 封装成一个类,统一管理:

方法作用
onUpdate(callback)注册每帧回调,接收 (delta, elapsed)
registerDisposable(resource)注册资源,dispose 时自动清理
start() / stop()控制动画循环
dispose()清理所有资源,防止内存泄漏

为什么不用全局变量?

  • 场景管理器可以复用(多场景切换)
  • 资源清理有统一入口
  • 回调列表方便扩展

05 窗口自适应

两个关键操作,顺序不能错:

// 1. 先更新相机宽高比
camera.aspect = width / height
camera.updateProjectionMatrix()  // 必须调用,否则不生效

// 2. 再更新渲染器尺寸
renderer.setSize(width, height)

06 像素比(Pixel Ratio)

renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  • window.devicePixelRatio:Retina 屏是 2 或 3
  • 为什么要 Math.min(..., 2)?超过 2 像素量翻倍但肉眼几乎看不出区别,白白浪费 GPU

07 速查表

Three.js 三件套

Scene(舞台)→ Camera(镜头)→ Renderer(投影仪)
     ↑                                ↓
  物体/灯光                        canvas 像素

动画循环

requestAnimationFrame(tick)
  → delta = (now - lastTime) / 1000
  → onUpdate(delta, elapsed)
  → renderer.render(scene, camera)

窗口自适应顺序

1. camera.aspect = w/h
2. camera.updateProjectionMatrix()
3. renderer.setSize(w, h)

08 复盘自测

  1. requestAnimationFrame 里为什么要用 delta time?

    因为不同设备帧率不同,用 delta time 让动画速度和时间挂钩而不是和帧率挂钩。

  2. 相机的 fov、near、far 分别控制什么?

    fov(视场角):视野的宽窄,像相机镜头的广角/长焦。 near:比这更近的物体不渲染。 far:比这更远的物体不渲染。


本文是 Three.js + GLSL + WebGPU 学习系列的第 1 篇笔记。课程评分:8.5/10。