跳到主要内容
3D Playground
Three.js 核心第 6 / 13 节

场景图与变换

入门WebGL

场景是一棵树

Scene 本身就是一个 Object3D,所有物体通过 parent.add(child) 挂成一棵树。每个 Object3D 都有三项局部变换:

  • position:相对父节点的位置
  • rotation / quaternion:相对父节点的旋转
  • scale:相对父节点的缩放

“相对父节点”是关键:子节点的最终位置 = 父节点的世界矩阵 × 子节点的局部矩阵。three.js 每帧把这些矩阵沿树向下相乘,得到每个物体的 matrixWorld。

枢轴技巧(pivot)

想让地球绕太阳转,最简单的办法不是每帧用三角函数算坐标,而是:

  1. 在太阳的位置放一个空的 Object3D 作为枢轴;
  2. 把地球放进枢轴,并设置 position.x = 半径;
  3. 每帧只旋转枢轴的 rotation.y。

枢轴一转,挂在下面的一切都跟着绕圈。月球同理:它的枢轴挂在地球的框架下面,所以它既绕地球转,又被地球带着绕太阳转。土星环挂在土星的框架上,继承了倾角,却不会跟着星球自转,因为自转只作用在星球的网格本身。

局部坐标 vs 世界坐标

月球框架的 position 永远是 (0.95, 0, 0),因为它相对父节点从未移动;但它在世界中的位置每帧都在变。要得到世界坐标,用 object.getWorldPosition(target)。示例把一个直接挂在场景下的绿色圆环移动到月球的世界坐标,状态栏同时打印两种坐标。

父节点的缩放和旋转会传给子节点

拖动 “scale (root)”:缩放根节点会让整个太阳系连同轨道一起缩放,因为缩放同样沿树传递。打开 “local axes”,每个星球上的小坐标轴显示的就是它自己的局部坐标系,你会看到地球的轴是倾斜的。

想把物体换一个父节点又保持它在世界中的位置不变?用 newParent.attach(object) 而不是 add。

核心 API

THREE.Object3Dobject.addobject.positionobject.rotationobject.scaleobject.getWorldPositionobject.matrixWorldscene.updateMatrixWorldTHREE.AxesHelper

动手试试

  1. 1给月球再加一颗“小卫星”:调用 addPlanet(moon.frame, {...}),看看三层嵌套的运动。
  2. 2把地球的 tilt 改为 0,对比月球轨道平面的变化。
  3. 3把土星环从 saturn.frame 移到 saturn.body 下,环会开始跟着自转,想想为什么。
  4. 4用 sun.attach(marker) 代替 scene.add(marker),观察世界坐标是否被保留。
运行中