14 · Vector3:插值、随机采样与参数化坐标

返回系列目录 · 上一章:几何运算 · 下一章:变换与投影

本章全部接口覆盖当前向量并返回 this。插值解决“处于两个状态之间哪里”,采样解决“按什么分布选择状态”,球坐标和柱坐标解决“用角度与半径更容易描述的位置”。三者在相机动画和粒子发射中经常组合。

lerp(v, alpha)

将当前值朝 v 移动 alpha 比例,公式为 this = (1-alpha)*this + alpha*v。起点 (0,0,0)、目标 (10,0,0)、alpha 为 0.2 时,结果是 (2,0,0);再调用一次得到 (3.6,0,0),因为本次起点已变。

v 不变;alpha 不会被限制,负值或大于 1 会外插。alpha=1 到达目标,0 保持原值。反复调用固定 alpha 是指数接近而非匀速运动,理论上通常不会在有限步内精确到达。

const alpha = 1 - Math.exp(-5 * dt);
position.lerp(target, alpha);

参数 5 的量纲为每秒,越大跟随越快。目标固定时,这种 alpha 对应连续指数衰减,基本消除刷新率造成的跟随差异;目标在帧间运动时仍有离散采样误差。

lerpVectors(v1, v2, alpha)

从固定两端计算 (1-alpha)*v1 + alpha*v2 并写入接收者,两个输入不被独立修改。v1 为 (0,0,0)、v2 为 (10,4,0)、alpha 为 0.5,得到 (5,2,0)。适合按时间进度实现确定时长动画:

output.lerpVectors(start, end, Math.min(elapsed / duration, 1));

调用者应保证 duration > 0、elapsed ≥ 0,并保持 start 独立于 output,否则起点随更新漂移。向量线性插值不保证长度:两个相反单位方向的中点是零;方向或旋转的插值需要额外约束,旋转通常交给 Quaternion.slerp。

random()

无参数,三个分量分别取 [0,1) 的伪随机数,得到单位立方体内部的均匀位置分布。它不是单位方向,也不是球体采样。

point.random().multiplyScalar(2).subScalar(1); // [-1,1)³

用于盒形区域发射粒子很方便。把盒内随机向量直接 normalize,方向分布会偏向立方体角附近,不能替代均匀球面采样。它使用 Math.random,没有种子参数;可重复实验需由调用者提供带种子的随机数生成和分量赋值。

randomDirection()

无参数,写入均匀球面随机单位方向,长度约为 1。r185 取均匀的高度 u∈[-1,1) 和方位角,再求横向半径 √(1-u²),使等面积区域获得相同概率。这里“均匀”指球面面积,不是欧拉角数值均匀。

velocity.randomDirection().multiplyScalar(3) 产生速率为 3 的各向同性速度。若需要半球,可按参考法线点积的符号翻转;若需要球体内部均匀位置,半径要取 R*cbrt(random),因为半径 r 内体积与 r³ 成正比。

setFromSpherical(s)

读取 THREE.Spherical 的 radius、phi、theta 转成 Vector3。s 不修改。例如 new THREE.Spherical(2, Math.PI/2, 0) 转成约 (0,0,2)。适合已经由相机控制器维护 Spherical 状态的情况;输入角度约定必须与 Three.js 一致。

setFromSphericalCoords(radius, phi, theta)

直接接收半径和两个弧度角:phi 从 +Y 极轴向下量,theta 在 XZ 平面从 +Z 朝 +X 增大。公式是:

x=rsin⁡ϕsin⁡θ,y=rcos⁡ϕ,z=rsin⁡ϕcos⁡θ. x=r\sin\phi\sin\theta,\quad y=r\cos\phi,\quad z=r\sin\phi\cos\theta.

r=2、phi=π/2、theta=π/2 时结果约 (2,0,0);phi=0 时是北极 (0,2,0),theta 此时不改变点。接口不自动限制角度或拒绝负半径,通常业务采用 r≥0、phi∈[0,π]。相机环绕时常把 phi 限制在两极之外,避免观察方向与 up 平行。

setFromCylindrical©

读取 THREE.Cylindrical 的 radius、theta、y,转成当前向量。例如 new THREE.Cylindrical(2,0,3) 得 (0,3,2)。c 不修改;其 y 是高度而不是第二个角,适合环形楼层排布或螺旋路径。

setFromCylindricalCoords(radius, theta, y)

直接接收水平半径、方位角(弧度)和高度:

x=rsin⁡θ,y=y,z=rcos⁡θ. x=r\sin\theta,\quad y=y,\quad z=r\cos\theta.

(radius=2,theta=π/2,y=3) 得约 (2,3,0)。radius 只控制到 Y 轴的距离,不是到原点的距离;同例总长度为 √13。改变 theta 得圆,令 y 随 theta 线性增加得螺旋;沿螺旋的运动速度仍取决于参数化方式。

实践:围绕展品计算相机轨道

import * as THREE from 'three';

const offset = new THREE.Vector3();
const center = new THREE.Vector3(0, 1, 0);

function orbitPosition(theta, out) {
  offset.setFromSphericalCoords(5, Math.PI / 3, theta);
  return out.copy(center).add(offset);
}

const worldCameraPosition = new THREE.Vector3();
orbitPosition(Math.PI / 2, worldCameraPosition);
console.log(worldCameraPosition.toArray()); // 约 [4.330, 3.5, 0]

球坐标结果是相对轨道中心的偏移,所以最后要加 center。若赋值给相机 position,应保证相机没有带变换的父节点,或先把世界位置转换到父节点空间。改变 radius 会改变轨道距离;改变 phi 会改变俯仰,theta 控制水平绕行。

练习(暂不附答案)

  1. 计算从 0 朝 10 连续调用三次 alpha=0.5 的 lerp,并与固定端点的 lerpVectors 比较。
  2. 实现一个精确持续两秒的位置动画,保证结束时写入终点,并处理 duration 为零的策略。
  3. 生成一万条 randomDirection,统计 Y 分量均值和长度范围;这能验证哪些性质,又不能证明哪些性质?
  4. 用柱坐标生成三圈螺旋楼梯的台阶中心,使每圈升高 2 米。