Align UTM Coordinates to glTF Local Origin
This article provides a direct guide on converting Universal Transverse Mercator (UTM) coordinates into glTF-compatible local origin spaces. You will learn how to handle floating-point precision limitations, reorient axes from UTM's Z-up standard to glTF’s Y-up system, apply translation offsets, and retain spatial reference metadata for 3D runtime applications.
1. Select a Local Reference Anchor
UTM coordinates are projected in meters with large absolute values
(e.g., hundreds of thousands of meters in Easting, millions in
Northing). Directly loading these values into glTF causes severe vertex
jitter because GPUs process glTF geometry using 32-bit single-precision
floating-point numbers (float32).
To resolve this, select a local reference origin, often referred to as a "Relative to Center" (RTC) anchor point:
\[\mathbf{P}_{\text{origin}} = (E_0, N_0, H_0)\]
Where:
- \(E_0\) = Reference Easting
- \(N_0\) = Reference Northing
- \(H_0\) = Reference Height (ellipsoidal or orthometric elevation)
The anchor should typically be the bounding box center or minimum corner of your dataset.
2. Translate Coordinates (64-Bit Precision)
Perform the coordinate subtraction using 64-bit double-precision
arithmetic (float64) prior to writing data into glTF binary
buffers.
For each vertex \((E, N, H)\):
\[\Delta E = E - E_0\] \[\Delta N = N - N_0\] \[\Delta H = H - H_0\]
This operation normalizes the geometry around \((0, 0, 0)\), keeping vertex values small enough to preserve precision when downcast to 32-bit floats.
3. Map Coordinates to the glTF Axis Convention
UTM and glTF utilize different coordinate axis standards:
- UTM Convention (Right-Handed, Z-Up):
- \(+X\) = Easting
- \(+Y\) = Northing
- \(+Z\) = Elevation / Up
- glTF Convention (Right-Handed, Y-Up):
- \(+X\) = Right (East)
- \(+Y\) = Up (Elevation)
- \(+Z\) = Backward / South (pointing out of the screen, making \(-Z\) North)
Apply an axis-swapping transformation matrix to align the translated UTM offsets with the glTF coordinate space:
\[\begin{bmatrix} X_{\text{glTF}} \\ Y_{\text{glTF}} \\ Z_{\text{glTF}} \end{bmatrix} = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 0 & 1 \\ 0 & -1 & 0 \end{bmatrix} \begin{bmatrix} \Delta E \\ \Delta N \\ \Delta H \end{bmatrix}\]
This yields:
- \(X_{\text{glTF}} = \Delta E\)
- \(Y_{\text{glTF}} = \Delta H\)
- \(Z_{\text{glTF}} = -\Delta N\)
4. Apply Grid Convergence Correction
UTM projections exhibit grid convergence—the angular difference between Grid North and True North—which varies depending on the distance from the central meridian of the UTM zone.
If your 3D application requires alignment with true geographic directions rather than the projection grid:
- Calculate the grid convergence angle (\(\gamma\)) for the anchor coordinate.
- Rotate the glTF vertices around the local \(+Y\) (Up) axis by \(-\gamma\):
\[\begin{bmatrix} X' \\ Z' \end{bmatrix} = \begin{bmatrix} \cos(-\gamma) & -\sin(-\gamma) \\ \sin(-\gamma) & \cos(-\gamma) \end{bmatrix} \begin{bmatrix} X_{\text{glTF}} \\ Z_{\text{glTF}} \end{bmatrix}\]
5. Store Georeferencing Metadata
Because the local glTF model is centered at \((0, 0, 0)\), you must store the original UTM zone, datum (e.g., WGS84), and anchor \((E_0, N_0, H_0)\) to position the asset accurately in world-space engines:
- Node Transforms: Store the global coordinates in the root node's translation matrix if the consuming runtime engine supports 64-bit scene nodes.
- glTF Extras / Extensions: Add a custom JSON object
to the
asset.extrasorscene.extrasfield detailing the EPSG code and translation vector:
{
"asset": {
"version": "2.0",
"extras": {
"georeference": {
"crs": "EPSG:32632",
"origin": [500000.0, 4649776.0, 120.0]
}
}
}
}