07 · 点积:朝向、投影与有效分量
1. 为什么分量相乘再相加
设 a、b 是同一正交归一坐标系中的向量,即三个基向量两两垂直且长度为 1。点积有两个等价表达:
第一种适合计算,第二种解释几何意义。二维只需去掉 z 项。可以从余弦定理理解等价性:一方面展开 |a-b|² 得到 |a|²+|b|²-2Σaᵢbᵢ;另一方面余弦定理给出 |a|²+|b|²-2|a||b|cosθ,对比即可得到公式。
向量长度非零时,点积正表示夹角小于 90°,零表示垂直,负表示超过 90°。零向量与任何向量点积都是零,但它没有方向,不能据此说它拥有一个确定的直角朝向。
2. 拉行李:朝前的有效作用有多少
假设你用大小 10 N、与地面前进方向成 60° 的力拉行李。前进单位方向为 u,拉力为 F,则:
这个数是带符号的标量投影:沿 u 方向的力为正,反向为负。拉力在该方向上的向量分量是 (F·u)u。
import * as THREE from 'three';
const forward = new THREE.Vector3(1, 0, 0);
const force = new THREE.Vector3(5, 5 * Math.sqrt(3), 0);
const usefulForce = force.dot(forward); // 5
const forwardForce = forward.clone().multiplyScalar(usefulForce);
如果行李水平前进 3 米,力做的功是 F·displacement = 15 J。这里位移无需归一化,因为长度本来就应该参与结果。由此可见,“点积前总要归一化”是错误规则;是否归一化取决于你想保留什么物理量。
对于非单位向量 b,a 在 b 上的投影为:
分母抵消 b 的长度影响;b 为零时几何投影方向未定义,需要业务约定。
3. 角色视野:通常不需要 acos
角色世界前向 f 为单位向量,角色到目标的世界位移为 d。视野总角度 90° 意味着中心线左右各 45°。比较余弦即可,无须先转换成角度:
const offset = new THREE.Vector3();
function inView(actorWorld, forwardWorld, targetWorld, range, fullFov) {
offset.subVectors(targetWorld, actorWorld);
const distanceSq = offset.lengthSq();
if (distanceSq > range * range) return false;
if (distanceSq < 1e-12) return true; // 本例约定:重合算在范围内
if (forwardWorld.lengthSq() < 1e-12) return false;
const cosine = forwardWorld.dot(offset) /
Math.sqrt(forwardWorld.lengthSq() * distanceSq);
return cosine >= Math.cos(fullFov / 2);
}
输入角度使用弧度,范围非负,总视角应在业务允许区间内。上面判断的是三维圆锥;如果只限制水平视野,应先将方向投影到水平面,并处理投影后长度为零的情况。它也不检查墙壁遮挡。
求真正的夹角时才用 Math.acos(clamp(cosine,-1,1))。浮点计算可能让理论上为 1 的值变成 1.0000000000000002,不钳制会得到 NaN。
4. 光照:面向光的表面为什么更亮
单位法线 n 与从表面指向光源的单位方向 l 同向时,一块表面接收的入射能量最多;斜过来时有效投影面积减少。简化 Lambert 漫反射项是:
vec3 normalUnit = normalize(vNormalView);
vec3 towardLight = normalize(lightPositionView - vPositionView);
float diffuse = max(dot(normalUnit, towardLight), 0.0);
两者必须在相同空间。方向若改为“光线传播方向”,需要取反再点积。varying 插值后的法线通常不是单位长度,因此片元阶段还要归一化。本式只是方向项,不包括材质能量守恒、距离衰减、阴影和颜色管理。
5. 日常选用规则
要判前后,通常只看点积符号;要判视角,比较归一化点积与余弦阈值;要取得沿某方向的速度,点积单位方向;要算做功,保留力与位移的实际长度。点积回答“有多少沿着另一个方向”,并不会提供三维空间中的左右符号。
练习(不附答案)
- 拉力 20 N,与前进方向夹角 30°,位移 4 米,分别求前向力和做功,并说明单位。
- 实现水平视野检测,处理目标正好在角色头顶的情况。
- 给定 n=(0,1,0),分别计算上方、侧方、下方光源的简化漫反射项。
- 构造两个长度不同、方向相同的向量,说明直接把原始点积当余弦为什么错误。