Three.js 日常开发中的向量与矩阵

这是一套面向日常开发问题的中文教程,共 41 章、164 道不附答案的练习。重点是 Vector3 与 Matrix3 的逐项接口讲解,同时覆盖坐标转换、Euler、Quaternion、Object3D、相机、交互、光照和 Shader 数据流。

目标是让你能解释“公式为什么这样写、输入在哪个空间、结果为什么对应这个视觉效果”,并把几何工具组合成可以维护的业务代码。数学入门读者可顺序阅读,有具体需求时直接使用下面的问题导航。

版本与约定

怎么读

从基础开始: 01–09 建立坐标与几何直觉 → 10–21 系统学习重点接口 → 22–25 掌握旋转和层级 → 选择 26–37 案例 → 38–41 补齐 Shader 与工程实践。

按问题学习: 先读对应案例,遇到不熟悉的公式或接口,再查基础章与接口索引。不必为了实现拖拽先通读所有随机采样接口。

日常问题 建议阅读
物体在父节点下,怎样获取或设置世界位置? 03 局部与世界空间、25 Object3D
camera space、world space、NDC 有什么区别? 04 相机空间、05 裁剪与 NDC、06 完整转换
点积、叉积到底解决什么问题? 07 点积、08 二维叉积、09 三维叉积
给模型加 HTML 标注,缩放窗口后位置不对 26 HTML 标签
鼠标拖拽物体,按下时突然跳动 27 拾取与拖拽
不同大小模型如何自动完整显示? 28 自动取景
角色越靠近目标越慢,或者高刷新率跑得更快 14 插值、29 角色运动
目标在左边还是右边、是否位于视野范围? 30 导航与视野
如何围绕目标旋转、让相机平滑跟随? 24 旋转组合、31 跟随相机
如何沿斜坡或墙面移动? 32 投影运动
反射以后速度不对,或穿过了地面 33 弹球、40 数值稳定性
怎样测量点到边的距离、把零件吸附到插槽? 34 测量与吸附
模型拉伸后光照异常 20 法线矩阵、35 光照修复
UV 旋转方向为什么和图案看起来相反? 21 UV 矩阵、36 纹理编辑
如何向任意方向发射均匀分布的粒子? 37 粒子发射器
JavaScript 的 Vector3 如何变成 GLSL 的 vec3? 38 Shader 数据流
应该把计算放在 CPU 还是 GPU? 39 计算分工、41 排错与性能

完整目录与练习导航

每个链接对应一篇独立 Markdown 文档,练习位于各章末尾。

基础与坐标空间

  1. 点、向量、方向与单位
  2. Vector2 与 Vector4 快速入门
  3. Local Space 与 World Space
  4. Camera Space:把世界放到相机面前
  5. Clip Space、NDC 与屏幕坐标
  6. 坐标转换完整链路与逆向转换

点积与叉积

  1. 点积:朝向、投影与有效分量
  2. 二维叉积:左右、转弯与有向面积
  3. 三维叉积:垂直方向、法线与力矩

Vector3:七章完整接口详解

  1. 创建、访问、复制与数据交换
  2. 算术与运动计算
  3. 长度、距离、归一化与约束
  4. 点积、叉积、投影与反射
  5. 插值、随机采样与参数化坐标
  6. 旋转、矩阵变换与相机投影
  7. 从矩阵、欧拉角和颜色提取数据

Matrix3:五章完整接口详解

  1. 矩阵直觉、创建与存储
  2. 组合变换与乘法顺序
  3. 行列式、逆矩阵与转置
  4. 基底提取与法线矩阵
  5. 二维齐次变换与 UV 矩阵

旋转与 Three.js 变换体系

  1. Euler:用三个角描述旋转
  2. Quaternion:轴角、朝向与插值
  3. 旋转组合与表示转换
  4. Matrix4、Object3D 与父子层级

真实场景实践

  1. 给三维设备添加 HTML 信息标签
  2. 鼠标拾取与拖动家具
  3. 商品模型自动居中与完整取景
  4. 角色移动、追踪与平滑转向
  5. 小地图导航与目标视野检测
  6. 第三人称相机跟随与环绕
  7. 车辆沿斜坡移动、物体贴墙滑行
  8. 弹球反射与表面反弹
  9. 模型测量、最近点与装配吸附
  10. 修复模型缩放后的光照异常
  11. 纹理编辑器:平移、缩放与旋转
  12. 可定向的喷泉与粒子发射器

Shader 协作与工程实践

  1. JavaScript 数学数据如何进入 Shader
  2. CPU 与 GPU 计算的选择
  3. 浮点误差与退化情况
  4. 几何调试、性能与代码组织

如何运行代码

数值例子: 在 Vite 的 JavaScript 模块中使用 import * as THREE from 'three',再运行相关片段。许多短代码块承接同一章的变量,不能把整个系列所有代码块拼成一个文件;重复的变量名和不同场景本来就应该隔离。

实战例子: 使用 examples/runtime.js 公共外壳。把它复制到你的练习入口脚本旁边,再将某章“完整核心代码”保存为 main.js。章节中的 import { createDemo } from './runtime.js' 就能解析。使用已有 Vite 练习页时,应替换原场景初始化与渲染循环,避免同时创建两个 renderer。

运行页面需有一个已确定尺寸的容器。默认是 body,练习入口样式可以设置 html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; };也可以调用 createDemo({ parent: 容器元素 }),由页面为容器指定尺寸。不要把未设高度的空 body 当成有效视口。

外壳 API:

返回值 契约
THREE Three.js 模块命名空间
scene 空场景,无预设灯光或地面
camera PerspectiveCamera,60°,near=0.1、far=1000,初始位置 (6,5,8),看向原点
renderer WebGLRenderer,DPR 上限为 2,自动响应容器与窗口尺寸
onFrame(callback) 注册渲染前回调 (dt, elapsed),返回退订函数;时间单位为秒
onDispose(callback) 注册 DOM、事件等自定义释放逻辑,返回注销函数
dispose() 停止循环,移除监听与画布,释放独立场景中的常见几何体、材质与纹理

dt 上限为 0.05 秒,elapsed 是截断后累计的模拟时间。这能减轻后台恢复时的大步跳跃,但不保证与真实时钟同步,也不是完整物理引擎。几何体或材质跨场景共享时,不应直接套用这个独立示例的资源所有权策略。

实战章节可把 dispose 加入解构,并在入口末尾加 if (import.meta.hot) import.meta.hot.dispose(dispose);,以便 Vite 热更新时释放旧场景。离开自定义路由或切换示例时同样应主动调用 dispose。

从项目根目录执行 yarn dev,打开你的练习页面;不要通过 file:// 直接加载模块。新建正式项目示例页时,还要遵守仓库的设计规范和入口登记规则。本次教程提供 Markdown 与公共外壳,不新增项目展示页面。

系统默认 Node 12 可以运行当前项目的 Vite 构建,但直接在该 Node 中导入 r185 的现代模块可能遇到语法错误。终端数学实验应使用能够解析该依赖的现代 Node;不要把浏览器模块的运行问题与 Vite 构建是否成功混为一谈。

公式与选择速查

目的 公式或选择 条件
从 A 朝 B B - A 两点处于同一空间
匀速位移 position += unitDirection * speed * dt 非零方向,单位一致
前后与夹角 `a·b = a
二维左右 ax*by - ay*bx 先声明轴向与参考顺序
三角形法线 (B-A)×(C-A) 非共线;方向依赖绕序
投影到单位方向 u (v·u)u u 为单位向量
投影到平面 v - (v·n)n n 为单位法线
镜面反射 v - 2(v·n)n n 为单位法线
法线变换 (A⁻¹)ᵀn 后归一化 A 为可逆线性部分
局部到裁剪 P * V * M * vec4(p,1) 右侧先作用
裁剪到 NDC clip.xyz / clip.w w 非零,注意可见性
页面点到世界 NDC → 射线 → 与几何体求交 仅 x/y 不足以确定深度
稳定朝向插值 Quaternion 的 slerp/rotateTowards 有效单位四元数

接口索引

下方索引按类与章节列出全部 Vector3、Matrix3 公共接口。点击章节可找到对应独立小节。

类型 章节 接口与属性
Vector3 10 章 constructor()、set()、setScalar()、setX()、setY()、setZ()、setComponent()、getComponent()、clone()、copy()、equals()、fromArray()、toArray()、fromBufferAttribute()、x/y/z、isVector3、[Symbol.iterator]()
Vector3 11 章 add()、addScalar()、addVectors()、addScaledVector()、sub()、subScalar()、subVectors()、multiply()、multiplyScalar()、multiplyVectors()、divide()、divideScalar()、negate()
Vector3 12 章 lengthSq()、length()、manhattanLength()、normalize()、setLength()、distanceTo()、distanceToSquared()、manhattanDistanceTo()、min()、max()、clamp()、clampScalar()、clampLength()、floor()、ceil()、round()、roundToZero()
Vector3 13 章 dot()、cross()、crossVectors()、angleTo()、projectOnVector()、projectOnPlane()、reflect()
Vector3 14 章 lerp()、lerpVectors()、random()、randomDirection()、setFromSpherical()、setFromSphericalCoords()、setFromCylindrical()、setFromCylindricalCoords()
Vector3 15 章 applyEuler()、applyAxisAngle()、applyQuaternion()、applyMatrix3()、applyNormalMatrix()、applyMatrix4()、transformDirection()、project()、unproject()
Vector3 16 章 setFromMatrixPosition()、setFromMatrixScale()、setFromMatrixColumn()、setFromMatrix3Column()、setFromEuler()、setFromColor()
Matrix3 17 章 constructor()、set()、identity()、copy()、clone()、equals()、fromArray()、toArray()、elements、isMatrix3
Matrix3 18 章 multiply()、premultiply()、multiplyMatrices()、multiplyScalar()
Matrix3 19 章 determinant()、invert()、transpose()、transposeIntoArray()
Matrix3 20 章 extractBasis()、setFromMatrix4()、getNormalMatrix()
Matrix3 21 章 makeTranslation()、makeRotation()、makeScale()、translate()、rotate()、scale()、setUvTransform()

本次验证范围

编写完成后已检查 41 个章节文件、164 道练习、110 个 JavaScript 代码块语法和全部本地文档链接;Vector3、Matrix3 方法标题与 r185 源码逐项对照无遗漏。关键数学与实战逻辑通过数值断言验证。

第 26–38 章及第 41 章的完整场景代码,使用公共外壳在本机 Edge 无头浏览器中分别以 1280×720、390×844 运行,共 28 次:未出现 JavaScript 或 Shader 编译错误,画布尺寸及销毁检查通过,并查看了光照、纹理与数据流示例截图。这里验证的是示例基本运行,不代表全部练习已实现,也不代表所有交互、设备和性能条件已覆盖。

项目 yarn build 通过;现有构建仍提示空入口 chunk 和大于 500 KiB 的 chunk,这些不由本次文档新增引起。文档中的代码块不会自动成为 Vite 构建入口,因此构建结果与示例运行检查分别记录。

资料与范围

接口核对来源:Vector3 源码、Matrix3 源码;安装依赖后本地源码链接才可用。

官方参考:Vector3、Matrix3、Euler、Quaternion。在线文档可能跟随新版本变化,本系列遇到差异以所标注的本地版本为准。

正文中的光照、碰撞和粒子模型用于讲清数学及常规实现;相关章节会说明与完整物理引擎、复杂渲染管线之间的边界。练习优先要求预测、验证、排错与组合应用,避免只记住接口名称。