How Group Scale and Rotation Affects Children in Three.js
In Three.js, grouping objects using THREE.Group or
THREE.Object3D allows you to manipulate multiple meshes as
a single unit. When you scale or rotate a parent Group, these
transformations are automatically propagated to all child objects
through hierarchical matrix multiplication, altering their world space
position, rotation, and size while preserving their local transformation
properties.
The Mechanism: Matrix Inheritance
Three.js uses a hierarchical scene graph where every 3D object
inherits transformations from its parent. Each object has two primary
matrices: * matrix: The local
transformation matrix (relative to its parent). *
matrixWorld: The global transformation
matrix (relative to the scene origin).
When you modify the rotation or scale of a parent Group, the child’s
local properties (child.rotation, child.scale,
and child.position) remain completely unchanged. However,
during the rendering loop, Three.js updates the child’s
matrixWorld by multiplying the parent’s
matrixWorld with the child’s local matrix:
\[\text{child.matrixWorld} = \text{parent.matrixWorld} \times \text{child.matrix}\]
This mathematical relationship dictates exactly how rotation and scaling behave.
What Happens During Parent Rotation?
When you rotate a parent Group:
- Pivot Point Alignment: The child objects rotate around the parent Group’s origin \((0, 0, 0)\), not their own individual origins. The parent’s origin acts as the pivot point for the entire group.
- World Position Update: Unless a child is positioned exactly at \((0, 0, 0)\) in local space, its world position will change as it orbits the parent’s origin.
- World Rotation Update: The physical orientation of
the child in the 3D scene changes. To retrieve the actual rotation of a
child in world space, you must use
child.getWorldQuaternion()orchild.getWorldDirection(), as thechild.rotationproperty will only show its local offset.
What Happens During Parent Scaling?
When you scale a parent Group:
- Relative Distance Scaling: The distance between the child objects and the parent’s origin scales proportionally. If you double the scale of the parent, a child positioned at local coordinate \((2, 0, 0)\) will now be at world coordinate \((4, 0, 0)\).
- Visual Size Scaling: The visual size of the child objects scales according to the parent’s scale factors.
- Local vs. World Scale: The
child.scaleproperty remains unchanged (e.g., \((1, 1, 1)\)), but its absolute size in the scene is modified. To find the actual visual scale, you must querychild.getWorldScale().
Important Caveat: Non-Uniform Scaling
Applying non-uniform scaling (different values for X, Y, and Z) to a
parent Group can introduce rendering issues. If a parent group is scaled
non-uniformly (e.g., group.scale.set(2, 1, 1)) and a child
object inside that group is rotated, the child will experience
shear or skewing distortion.
To avoid unwanted warping of child geometries, always try to use
uniform scaling (group.scale.set(2, 2, 2)) when working
with grouped objects that undergo independent rotations.