Z-up, Y-up, and the model that arrives on its side
Half of 3D thinks Z points up and half thinks Y does. The split runs along a clean line: it is CAD and DCC against realtime and the web. Which formats sit where, and where to fix it.
A model arrives lying on its side. You rotate it 90 degrees about X, export, and the next person gets it lying on its side again in the other direction. This is the oldest annoyance in 3D interchange and it comes from a genuine disagreement about which axis points up.
The split
It is not random. It runs along a clean line.
Z-up comes from engineering drawing and surveying, where you work on a plan, X and Y are the map, and Z is height above it. So CAD is Z-up: STEP, IGES, DXF, DWG, and by inheritance 3ds Max and Blender.
Y-up comes from screens. In 2D graphics X is across and Y is up the display; adding a third axis for depth makes Y stay up. So realtime graphics is Y-up: OpenGL, Direct3D, glTF, USDZ, Unity, Unreal and every web viewer.
Neither is wrong. A structural engineer wanting Z to be height and a graphics programmer wanting Y to stay where it was on screen are both being sensible.
Which formats say, and which assume
| Format | Up axis | Recorded in the file? |
|---|---|---|
| glTF, GLB | Y | No: the specification fixes it |
| USD, USDZ | Either | Yes, plus a metres-per-unit scale |
| COLLADA | Either | Yes, in the asset element |
| FBX | Either | Yes, in the global settings |
| OBJ | Y by convention | No |
| STL | Whatever wrote it | No |
| STEP, IGES | Z | No: it is the CAD convention |
| MAX | Z | No |
The formats that record it are the ones that cause the least trouble, and USD goes furthest by recording the scale as well, so a model authored in centimetres does not arrive a hundred times too big.
Where to do the rotation
Once, at the boundary, on the way into the format that assumes an axis. Converting Z-up CAD to glTF, apply the rotation in the conversion. Do not do it by rotating the root node and leaving the geometry as it was, because:
- Anything reading the file and ignoring node transforms, which includes plenty of tools, gets the wrong orientation.
- Bounding boxes, physics colliders and lightmap parameterisation are computed from the geometry, not the node.
- The rotation propagates: the next person adds their own and now there are two.
The correct transform from Z-up to Y-up is a rotation of minus 90 degrees about X: it sends (x, y, z) to (x, z, -y). The one people reach for first, plus 90, sends it to (x, -z, y) and gives you a model that is upright but mirrored front to back. Both look plausible in a viewport with no reference object, which is exactly why the mistake survives so long.
Units, while you are there
The other half of the same problem. glTF is metres. STL and OBJ have no unit at all. STEP records one. 3ds Max has a system unit that is a per-scene setting nobody remembers changing. A model that arrives at 1000 times scale is a millimetre file read as metres, and a model at 0.0254 is inches read as metres.
Fix scale in the same place you fix the axis, and write down what the file is in.
How this site handles it
Which axis a format stands up along is recorded in the format registry, so a .max or a .step arrives the right way up in the viewer and the renderer rather than on its side, and there is an override in case the file disagrees with its own format's convention, which happens more often than it should. The converter applies the rotation to the geometry rather than to a wrapper node, for the reasons above.
Sources
Primary references for the claims above. Where a specification exists, it is cited in preference to anybody's summary of it.
- glTF 2.0 specification, Khronos GroupglTF fixes Y-up and metres, which is why it so often ends up the arbiter.
- Universal Scene Description documentation, Pixar and the Academy Software FoundationUSD records an up axis and a metres-per-unit scale in the stage rather than assuming one.
- Open Asset Import Library (Assimp), GitHubA useful survey of what each format assumes, gathered in one codebase.