场景是一棵树
Scene 本身就是一个 Object3D,所有物体通过 parent.add(child) 挂成一棵树。每个 Object3D 都有三项局部变换:
position:相对父节点的位置rotation/quaternion:相对父节点的旋转scale:相对父节点的缩放
“相对父节点”是关键:子节点的最终位置 = 父节点的世界矩阵 × 子节点的局部矩阵。three.js 每帧把这些矩阵沿树向下相乘,得到每个物体的 matrixWorld。
枢轴技巧(pivot)
想让地球绕太阳转,最简单的办法不是每帧用三角函数算坐标,而是:
- 在太阳的位置放一个空的
Object3D作为枢轴; - 把地球放进枢轴,并设置
position.x = 半径; - 每帧只旋转枢轴的
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给月球再加一颗“小卫星”:调用 addPlanet(moon.frame, {...}),看看三层嵌套的运动。
- 2把地球的 tilt 改为 0,对比月球轨道平面的变化。
- 3把土星环从 saturn.frame 移到 saturn.body 下,环会开始跟着自转,想想为什么。
- 4用 sun.attach(marker) 代替 scene.add(marker),观察世界坐标是否被保留。
还没有输出。console.log 的内容会显示在这里。
运行中