Model Optimisation

Patrick Morrison, 17 August 2025

Learn how to optimise photogrammetry models for smooth WebXR performance on headsets and mobile devices. This guide covers mesh compression, texture optimisation, and practical file size targets for web deployment.

Optimising Photogrammetry 3D Models for WebXR on Meta Quest 3

BelowJS uses .glb files to store 3D models. This is the binary (single-file) version of glTF, an open standard for sharing 3D data often thought of as the "JPEG of 3D." All photogrammetry packages we use can export .glb files, so we have an opinionated pipeline for preparing them for the web.

Model exports are rarely well-optimised by default. In WebXR they benefit substantially from mesh and texture compression. This 1) reduces file size (faster loads) and 2) greatly reduces GPU memory (VRAM), which is crucial for running large, detailed models on a headset or phone.

Below is how we approach optimisation, plus a script to do it for you.

Exporting from Metashape:

Assuming you have a scaled and levelled model, it is simple to export from Agisoft Metashape. It will be similar in other packages.

In Metashape, File → Export → Export model… Binary gITF (*-glb)

This .glb is a single file that contains the mesh and the textures - it's an alternative to formats like like .obj, .fbx, .stl, etc. We have had the best results with 1,000,000 polygon models using 4 × 4K textures, which stays under 50MB - an acceptable limit for most modern internet connections. In some cases it might be worth considering a much lower target - say under 10MB.

Mesh Compression

Draco is a de-facto standard for glTF geometry compression. It's well supported in Three.js, and can significantly reduce mesh sizes with only a small decode cost on model load. For technical details on using Draco with Three.js, see the DRACOLoader documentation.

This can be done in one line with gltf-transform, which also produced mipmaps:

npx gltf-transform draco input.glb output.glb

For many models the default settings are ideal, however, Metashape models with multiple textures can end up with distracting seams caused by imperfect compression. This is because the underlying representation of model with 4 texture tiles is 4 separate models, and slight changes to the mesh become very obvious in VR.

The solution is to increase the quality of quantization, from a default of 14-bit to 20-bit. We have had no visible seams on models compressed at this setting. With this method highly-detailed 1 million polygon models can be viewed on Meta Quest 3 headsets, with plenty of room for other processing.

npx gltf-transform draco tmp-ktx.glb output.glb \
  --method sequential \
  --encode-speed 0 --decode-speed 0 \
  --quantize-position 20 \
  --quantize-normal 20 \
  --quantize-color 20 \
  --quantize-texcoord 20 \
  --quantize-generic 20

Texture Compression

The biggest gains in VRAM (GPU memory) efficiency come from texture compression. We use the GPU-native KTX2 ETC1S (see Don McCurdy's web texture formats guide for more information):

npx gltf-transform etc1s input.glb tmp-ktx.glb --quality 64

For a model with four 4K textures and ~1,000,000 polygons, this workflow typically reduces size from ~65 MB on disk and ~1.5 GB VRAM to ~45 MB on disk and ~390 MB VRAM—about a 30% cut on disk and 75% less VRAM. Without this, the headset often crashes on load.

Model Disk before Disk after VRAM before VRAM after
Junee 71.2 MB 44.4 MB 1.5 GB 392.4 MB
Clipper 64.2 MB 48.5 MB 1.5 GB 390.1 MB
Sesa 61.3 MB 43.9 MB 1.5 GB 388.0 MB

below-optimiser

We use a utility for this process.

./below-optimiser pack input.glb

This will apply 20-bit Draco mesh compression and KTX2 texture compression, then write out <input>-quest.glb.

For texture editing - normally light colour correction, we also have unpack:

./below-optimiser unpack input.glb

This will create a folder of the form path/to/input_edit. You can then edit these textures, and pack that folder.

./below-optimiser pack path/to/input_edit

It will also detect normal maps, and link them, as long as they are named to the form *normal1.*, *normal2.*, etc. If you originally had .png textures, but replaced them with jpegs, it will also detect that and adjust the gltf references before packing.

Download below-optimiser at https://github.com/patrick-morrison/below-optimiser
It has been tested on macOS, and Windows Subsystem for Linux (WSL).

Next Steps

Once your model is optimised, you can load it into BelowJS and experience it in VR with a compatible headset.