Baking Photogrammetry Details into glTF Normal Maps
This article explains the technical pipeline for transferring surface detail from high-polygon photogrammetry scans onto lightweight, real-time 3D assets. It covers generating the optimized low-poly proxy, establishing clean UV layouts, casting surface vectors during the normal map bake, aligning with glTF format standards, and packing the resulting textures for efficient web and mobile delivery.
1. Generating High-Poly and Low-Poly Meshes
Photogrammetry software constructs dense point clouds and converts them into raw, unoptimized 3D meshes that often contain millions of polygons and irregular topology. Because raw scans cannot be rendered efficiently in real-time environments, a dual-mesh workflow is required:
- High-Poly Source: The raw, high-density photogrammetric scan containing the microscopic surface details, crevices, and geometric imperfections.
- Low-Poly Target: A retopologized mesh generated via decimation algorithms or manual quad-retopology. This mesh mimics the macroscopic silhouette of the original scan while drastically lowering polygon counts to tens of thousands (or fewer) of triangles.
2. UV Unwrapping and Mesh Alignment
Before baking can occur, the low-poly target must be UV unwrapped. The UV coordinates dictate how a 2D image maps to the 3D surface. To ensure quality baking:
- UV islands should maximize texture space without overlapping.
- Hard geometric edges should align with UV seams to prevent shading artifacts.
- The low-poly model and the high-poly scan must occupy the exact same coordinate space, sharing identical positions, rotations, and scales.
3. Ray Casting and Normal Map Baking
Normal map baking transfers the fine directional vectors of the high-poly mesh onto a flat 2D texture applied to the low-poly mesh.
A baking engine (such as Blender, Marmoset Toolbag, or Substance 3D Baker) shoots projection rays outward from the low-poly surface toward the high-poly surface using an inflated boundary known as a "cage." Where the ray intersects the high-poly surface, the engine records the angle of the high-poly surface normal.
This directional vector (\(X, Y, Z\)) is converted into an RGB color value:
- Red Channel: Tangent (\(X\) axis, left/right surface direction)
- Green Channel: Bitangent (\(Y\) axis, up/down surface direction)
- Blue Channel: Normal (\(Z\) axis, depth pointing directly away from the surface)
The resulting Tangent Space Normal Map creates the illusion of depth by recalculating surface lighting per pixel rather than per vertex.
4. Conforming to the glTF Specification
The glTF (GL Transmission Format) and GLB specifications define strict requirements for normal maps to render correctly across diverse WebGL and native engines:
- Tangent Space Standard: glTF relies on the MikkTSpace standard for calculating vertex tangents. Bakers must be set to MikkTSpace to avoid inverted or distorted shading seams.
- Green Channel Direction (OpenGL): glTF requires OpenGL-style normal maps, where the Green channel (\(+Y\)) points upward. If the map is baked in a DirectX format (where Green is \(-Y\) downward), the green channel must be inverted before export.
- Linear Color Space: Normal map textures in glTF are
interpreted linearly (
sRGB = false). Storing or reading normal maps with sRGB gamma curves will distort the light calculations.
5. Packaging and Lightweight Delivery
Once baked, the normal map is integrated into the glTF PBR (Physically Based Rendering) material stack alongside base color, metallic-roughness, and ambient occlusion maps.
To maximize loading speed and minimize file size over the network:
- The normal map is saved as an 8-bit PNG, JPEG, or compressed using WebP.
- For advanced production delivery, glTF extensions like
KHR_texture_basisuallow normal maps to be transcoded into universal GPU formats (KTX2 / Basis Universal), keeping memory footprints tiny on mobile devices. - The geometry and texture references are packaged into a single
binary
.glbcontainer, delivering photorealistic visual fidelity at a fraction of the raw scan's computational overhead.