普通视图

发现新文章,点击刷新页面。
昨天以前首页

OrbitControls 的完整原理

作者 big男孩
2025年11月28日 11:20

🎯 OrbitControls 的完整原理(最精简 + 最准确版)

OrbitControls 的核心目标很简单:

让相机围绕一个目标点(target)做旋转、缩放、平移,同时保持视角稳定。****

它的实现不是直接修改相机的 rotation,而是:


✅ 1. 使用球坐标系存储相机相对 target 的位置

OrbitControls 内部用 球坐标系 (spherical) 表示相机在球面上的位置:

spherical 属性 代表含义 决定什么
radius 相机到 target 的距离 缩放(远近)
theta 水平旋转(绕 Y 轴) 左右旋转
phi 垂直旋转(从 Y+ 向下量角) 上下旋转

OrbitControls 并不直接存相机 position,而是依赖 spherical。


✅ 2. 每次更新,都会根据 spherical 重新计算 camera.position

伪代码:

// 球坐标转换为世界坐标
offset.setFromSpherical(spherical);

// camera.position = target + offset
camera.position.copy(target).add(offset);

// 相机永远朝向 target
camera.lookAt(target);

也就是说:

相机的位置永远落在一个以 target 为圆心的球面上。****


✅ 3. 用户的三种交互对应修改 spherical 或 target

✔(1)旋转(左键)

修改 spherical 的角度:

theta ← 左右拖动
phi   ← 上下拖动

效果:相机绕 target 转圈。


✔(2)缩放(滚轮)

修改 spherical.radius:

radius += delta

效果:相机沿着射线(camera → target)前进或后退。


✔(3)平移(右键)

修改 target(以及 camera.position 同步移动):

target += panDelta
camera.position += panDelta

这样保持相机与 target 的相对距离不变,视野整体平移。


📌 Why?为什么不直接改 camera.rotation?

因为:

  • 欧拉角 rotation 会出现万向节锁 (gimbal lock)

  • 旋转结果顺序依赖 Euler 的 order

  • 不能保证相机固定绕某点旋转

  • 平移与旋转混合会很混乱

使用 spherical + lookAt(target) 是最稳定、最可控的方案。


🎨 图示(概念图)

          Y+
          |
          |      camera ●
          |       (theta, phi, radius)
          |        /
          |       /
          |      /
          |     /
          ●----+------------------ X+
         target

📌 相机始终在半径为 radius 的球面上

📌 用户的操作实质是改变球坐标的位置


🎯 OrbitControls 本质上做的两件事

无论用户怎么操作,都只是修改:

  1. camera.position****

  2. controls.target

最终调用:

camera.lookAt(target);

而不动 rotation。


📦 大总结(你可以直接放到文档里)

OrbitControls = 基于球坐标的相机系统,通过改变 spherical(旋转/缩放)和 target(平移),生成 camera.position,然后使用 lookAt 保持视角稳定。****

  • 旋转 = 改 theta 和 phi

  • 缩放 = 改 radius

  • 平移 = 改 target

相机不会直接修改 rotation,而是由 target 和 spherical 决定最终的相机朝向和位置。


❌
❌