01 · 点、向量、方向与单位

返回目录 · 下一章

本章目标:看到一段位置更新代码时,能够说明每个量表示什么、处于哪个空间、使用什么单位。后续公式默认右手坐标系、列向量,角度使用弧度。

1. 相同的三个数字,可以有不同的含义

new THREE.Vector3(3, 0, 4) 只是一个存储三个分量的对象。它可以表示世界位置、位移、速度、法线或 RGB 数据;类名并不能保证这些含义可以混用。

点描述“在哪里”,依赖坐标原点;位移描述“从这里到那里”,是两个点的差。设仓库中机器人的位置为 A,充电站为 B:

A=(1,0,2),B=(4,0,6),d=B−A=(3,0,4) A=(1,0,2),\quad B=(4,0,6),\quad d=B-A=(3,0,4)

沿位移 d 走,机器人能抵达充电站。把整个仓库平移 100 米后,A、B 的坐标改变,但它们的差不变。

import * as THREE from 'three';

const robot = new THREE.Vector3(1, 0, 2);
const station = new THREE.Vector3(4, 0, 6);
const displacement = new THREE.Vector3().subVectors(station, robot);
const destination = robot.clone().add(displacement);
console.log(displacement.toArray()); // [3, 0, 4]
console.log(destination.equals(station)); // true

这里必须保留原始位置,因此使用 clone() 和独立输出变量。station.sub(robot) 会直接修改 station,造成充电站悄悄移动。

常用的语义规则是:点减点得到位移,点加位移得到点,位移加位移仍是位移。两点直接相加虽能计算,却通常需要进一步除以 2 才能解释为中点。

2. 长度和方向分开管理

欧氏长度来自勾股定理:

∥d∥=dx2+dy2+dz2=5,d^=d/∥d∥=(0.6,0,0.8) \|d\|=\sqrt{d_x^2+d_y^2+d_z^2}=5,\qquad \hat d=d/\|d\|=(0.6,0,0.8)

单位向量只保留朝向。若速度为每秒 2 个世界单位,则速度向量为 direction * 2,而不是直接把 displacement 当成速度。后者会让离目标越远的机器人跑得越快。

const distance = displacement.length();
const direction = displacement.clone();
if (distance > 1e-8) direction.divideScalar(distance);
else direction.set(0, 0, 0); // 到达阈值内停止,不能把未归一化的小位移当朝向。
const velocity = direction.clone().multiplyScalar(2);
const dt = 0.1; // 秒
robot.addScaledVector(velocity, dt); // 位移 = 速度 × 时间

归一化不能从零向量中创造朝向。若机器人已经到站,业务应停止移动或保留上一次朝向,而不是期待数学库猜测一个方向。

3. 用量纲检查代码

Three.js 不强制米或厘米。项目应约定一个世界单位代表什么,并在模型导入边界转换。例如模型使用毫米,业务使用米,就将长度乘以 0.001。

量 建议单位 常见关系
位置、距离 世界单位,例如米 位置差得到位移
速度 世界单位/秒 乘 dt 得到位移
加速度 世界单位/秒² 乘 dt 得到速度变化
方向、法线 无量纲,通常单位长度 乘速度得到速度向量
角度 弧度 Math.PI 等于 180°

position.add(velocity) 缺少时间因子,帧率越高就跑得越快。distance < radius * radius 则把长度和长度平方比较,量纲已经不一致。比较平方距离时,应写 distanceToSquared(target) < radius * radius。

4. 分量不是绝对方向

x 表示相对于当前基底第一根轴的分量,不保证永远是屏幕右侧。角色转身后,它的局部右方向会改变,但世界 X 轴没有变。变量命名如 targetWorld、velocityLocal 可以提前暴露跨空间混用。

同一空间也不意味着同一种量:世界位置不能直接与世界法线相加来表达物理运动,除非明确将单位法线乘上一个位移长度,例如“沿法线抬高 0.01 米”。

练习(不附答案)

  1. 给定 A=(2,1,-1)、B=(-1,1,3),求位移、距离和单位方向,并用代码验证。
  2. 机器人每秒移动 3 米,分别使用 1/30 和 1/120 秒的步长模拟一秒。说明什么条件下终点应该相同。
  3. 写一个计算两点中点的函数,要求不修改输入对象。
  4. 排查 position.add(direction.multiplyScalar(speed)) 的问题:分别考虑帧率和重复修改 direction。