The Reality Tiler tiles photogrammetry and reality models into optimized 3D Tiles 1.1 tilesets for runtime streaming and visualization.

Quickstart guide



  • An OS that supports glibc 2.28 such as:

    • AlmaLinux 8.8 or later

    • Ubuntu 18.10 or later


  • Minimum OS: Windows 10 or Windows Server 2019

  • Visual C++ Redistributable for Visual Studio 2022. Download and run the installer.

Installing the license

A license is required to run tilers. The license file is provided separately from this packaged build.

Copy the license to the same directory as the tilers executable. By default, the executable is located at bin/tilers, so the license must be saved as bin/license.


Print help

./bin/tilers --help

Run the Reality Tiler

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json

Viewing tilesets

To view the output tilesets, a rendering engine that supports 3D Tiles 1.1 such as one of the following is required.

Viewing tilesets in CesiumJS

Tilesets produced by tilers can be viewed in CesiumJS with the help of a static server.

Setting up a static server for local development

For local development, there are plenty of static servers available. The example below will use the third-party npm module http-server, but any static server can be substituted.

To install http-server

npm install -g http-server
cd directory/with/tilesets/
http-server --port 8002 --cors

Note that:

  • http-server is intended for local development only, not production

  • The port number can be changed if desired.

  • Since and http://localhost:8002 are different origins, the static server must enable Cross-Origin Resource Sharing (CORS), hence the --cors flag

  • In the future, browsers may restrict access to localhost for security reasons, in similar manner to the CORS policy. See this http-server issue for more information.

Loading the tileset in CesiumJS

Once you have a server running, use Sandcastle to load and view the tileset.

const viewer = new Cesium.Viewer("cesiumContainer");

async function createTileset() {
  // This tileset was not georeferenced, so we must provide a model matrix at runtime.
  // Place the tileset at (longitude, latitude) = (0, 0) at 10 meters above the ellipsoid.
  const tilesetPosition = Cesium.Cartesian3.fromRadians(0, 0, 10);
  const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tilesetPosition);

  // Create the tileset primitive
  const tileset = await Cesium.Cesium3DTileset.fromUrl(

  // Add it to the scene

  // Zoom the camera to the tileset.
  await viewer.zoomTo(tileset);


You can similarly load the tileset into Unreal Engine, Unity, or Omniverse by copying the URL into the Cesium 3D Tileset object. For more information, you can follow this tutorial.

Reality Tiler

The Reality Tiler takes a large, textured 3D mesh and generates a 3D Tiles 1.1 tileset from it. This provides the following benefits:

  • The output tileset is a hierarchical level-of-detail (HLOD) representation of the mesh.

  • The HLOD structure enables optimized streaming and rendering across different types of devices and engines.

  • Geometry, texture and file compression can be applied to the tiles to reduce file size on disk and reduce network usage.

Command line example

./bin/tilers -i path/to/model.obj -o output/tileset.json

Frequently used options

Georeferencing tilesets

Georeference a tileset manually
# Position the model over downtown Chicago.
# longitude and latitude are in degrees.
# height is in meters above the WGS84 ellipsoid
./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --longitude -87.6298 --latitude 41.8781 --height 100

There are options for setting the position, orientation and scale of the output:

Option Description


The longitude in EPSG:4979 coordinates (degrees) to set an origin for the data.


The latitude in EPSG:4979 coordinates (degrees) to set an origin for the data.


The ellipsoid height in EPSG:4979 coordinates (meters) to set an origin for the data.


The rotation in degrees from the local north direction where a positive angle is increasing eastward.


The rotation in degrees from the local east-north plane. Positive pitch angles are above the plane. Negative pitch angles are below the plane.


The rotation in degrees applied to the local east axis.


The amount to scale in the x direction.


The amount to scale in the y direction.


The amount to scale in the z direction.

Georeference a tileset with a coordinate reference system (CRS)

Often real-world data is measured with respect to a coordinate reference system. This includes map projections, geodetic coordinates (i.e. longitude, latitude, height), and other methods of assigning 3D coordinates to points on the earth’s surface. tilers uses the PROJ library to parse data from these coordinate reference systems.

# Position the model above Center City Philadelphia, using the
# Pennsylvania State Plane South coordinate system (EPSG:2272)
./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --input-crs EPSG:2272 --input-crs-origin-x 2693526.706772 --input-crs-origin-y 236133.329462 --input-crs-origin-z 100

In the above example, the --input-crs specifies the CRS to use. The --input-crs-origin-(x|y|z) options set an origin point.

The CRS string must be a format supported by the PROJ library. Common examples include:

  • EPSG Codes. The European Petroleum Survey Group (EPSG) maintains a database of coordinate reference systems and other geodetic parameters, each with a unique identifier (the EPSG code). This is often the easiest option for widely-used coordinate reference systems.

  • The Open Geospatial Consortium (OGC) Well-Known Text (WKT) format. While more verbose, WKT strings are more expressive in describing the coordinate reference system.

  • PROJ strings. This is PROJ’s older, but still widely used, format for specifying a CRS.

For more information on what CRS strings that PROJ supports, see the proj_create() documentation

Georeferencing ContextCapture input

ContextCapture models often include georeferencing information in a sidecar XML file. The Reality Tiler does not currently parse such files, but the georeferencing information can be specified with command line flags.

For example, suppose the input file contained the following georeferencing information:

<?xml version="1.0" encoding="utf-8"?>
<ModelMetadata version="1">
  <!--Spatial Reference System-->
  <!--Origin in Spatial Reference System-->
  <!-- ... -->

The <SRS> tag ("Spatial Reference system") corresponds to the --input-crs flag. The SRSOrigin corresponds to the --input-crs-origin-(x|y|z) flags.

For the example above, the corresponding tilers command would look like this:

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --input-crs EPSG:28355+5773 --input-crs-origin-x 319000 --input-crs-origin-y 5813000 --input-crs-origin-z 0
A future release of the 3D Tiling Pipeline will automatically read sidecar files that provide CRS information.


The 3D Tiling Pipeline offers a variety of compression options for the output. All files can be compressed using gzip compression. Furthermore, geometry and texture compression can be applied for glTF models in the output.

File compression: gzip

All files in the output tileset can be compressed with general-purpose gzip compression using the --gzip flag:

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --gzip
Geometric compression: Draco
This option is enabled by default in tilers

The Google Draco Library provides compression designed for 3D models. It can be used to compress glTF attributes using the KHR_draco_mesh_compression glTF extension.

Draco is optimized for small file size. It may reorder vertices to achieve better compression.

Draco compresion includes quantizing the positions to a grid. This is a lossy compression technique that trades precision for fewer bits.

To use Draco compression, set the --geometric-compression DRACO command line flag.


./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --geometric-compression DRACO

By default, models are quantized to 1 millimeter precision. To control the quantization precision, use the --compression-precision-meters flag. For example, to change the quantization to 1 centimeter precision, the command would be:

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --geometric-compression DRACO --compression-precision-meters 0.01
Geometric compression: Meshopt

Meshopt is a different compression algorithm for 3D models that is optimized for runtime performance. glTF attributes can be compressed using the EXT_meshopt_compression extension. This extension is combined with KHR_mesh_quantization (see the Quantization) for more efficient (lossy) compression.

To enable both meshopt and quantization, set --geometric-compression MESHOPT.


./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --geometric-compression MESHOPT

By default, models are quantized to 1 millimeter precision. To control the quantization amount, use the`--compression-precision-meters` flag. For example, to change the quantization to 1 centimeter precision, the command would be:

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --geometric-compression MESHOPT --compression-precision-meters 0.01
Geometric compression: quantization

For applying vertex quantization without compression, the Reality Tiler supports the KHR_mesh_quantization as a standalone extension. This quantizes positions to a grid specified by --compression-precision-meters, which are then decoded at runtime on the GPU. Quantized positions are stored as integers rather than floats, which saves storage space.

To enable quantization, set --geometric-compression QUANTIZATION as in the following example:

./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --geometric-compression QUANTIZATION --compression-precision-meters 0.01
Color texture compression: KTX2
This option is enabled by default in tilers

For textures containing color information, the tiler supports the Khronos Texture (KTX) compressed textures. KTX v2.0 is an image container format that supports Basis Universal supercompression. These textures are optimized for rapid transcoding to GPU texture formats, which vary by device and manufacturer. This texture compression is applied to glTF models using the KHR_texture_basisu glTF extension.

# Texture compression with KTX2.
./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --color-texture-compression KTX2

When --color-texture-compression is set to KTX2, tilers uses the ETC1S mode of BasisU for compressing textures. This format uses a lossy compression method that produces smaller textures than JPEG. It also has a small footprint at runtime, as it transcodes to GPU compressed texture formats.

Texture compression: JPEG

The tiler supports JPEG images for textures via the --color-texture-compression JPEG option. JPEG images are smaller than PNG, and are more widely supported than KTX2. However, JPEG images use lossy compression.


./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --color-texture-compression JPEG
Texture compression: PNG

The tiler supports PNG textures for textures via the --color-texture-compression PNG option. PNG images use lossless compression. This preserves detail of textures without any artifacts at the cost of larger file size.


./bin/tilers -i example/data/duck/duck.obj -o output/duck/tileset.json --color-texture-compression PNG

Out-of-core processing

The 3D Tiling Pipeline uses disk space for out of core (OOC) processing of large datasets that do not fit in physical memory.

  • A solid-state NVMe is recommended for best performance.

  • Make sure the disk where tilers is run has enough free space. 5-10x the size of the input data is recommended.

  • If you interrupt tilers while running, please clean up temporary files with the name tilers_*.mmap. By default, these files are stored in the system temporary files directory, which are typically:

    • Linux: /tmp

    • Windows: C:\Users\<username>\AppData\Local\Temp

Frequently asked questions

Tileset is rotated 90 degrees

If the output tileset appears rotated 90 degrees from the correct orientation, check which axis is up for the original dataset, and set --input-up-axis accordingly. By default, the 3D Tiling Pipeline assumes a y-up coordinate system for glTF models and z-up coordinate system for OBJ models.

The --input-up-axis setting is mutually exclusive with the --input-crs option, as the latter assumes the data uses the coordinate system of the CRS

Tileset appears at the center of the earth

If the tileset appears at the center of the earth, the data is likely in a local coordinate system and needs an additional matrix transform at runtime to position and orient the tileset on the globe.

This can be done two different ways:

  1. Use the 3D Tiling Pipeline’s georeferencing options to position the tileset.

  2. At runtime, apply a model matrix to the tileset to position it on the globe. See the Viewing tilesets in CesiumJS section for more details.

Tileset appears in the wrong location on earth

If the tileset appears in space or somewhere else unexpected on the globe, the input data may require georeferencing. The CLI option --input-crs can be used to specify the coordinate reference system (CRS) as an EPSG code, a PROJ string, or a WKT string.

Technical reference

Input model requirements

The Reality Tiler accepts textured 3D models in glTF or OBJ format. There are further requirements, listed below.

glTF input requirements

a glTF input must meet the following requirements:

  • Primitives

    • glTF input models must have at least one mesh primitive

    • primitives must use the TRIANGLES mode

  • Attributes

    • Models must have a POSITION attribute

    • Textured models must also have a TEXCOORD_0 attribute

    • All other attributes are currently ignored (including vertex colors)

    • No glTF extensions for attributes (e.g. for compression) are currently supported.

    • All mesh primitives (across all input glTF assets combined) must have the same number of attributes.

  • Materials

    • Currently only baseColorTexture and baseColorFactor are supported.

    • baseColorTexture must use TEXCOORD_0

    • the KHR_materials_unlit extension will be propagated to the output.

  • Textures

    • Only PNG and JPEG formats are supported

    • No texture extensions are currently supported

    • If a model has no texture, or references a texture that is not found when a model is loaded, the Reality Tiler will use a 1x1 px white texture instead.

    • All texture wrap modes (wrapS/wrapT) are supported. However, repeated textures may require large amounts of virtual memory. For best results, use models with all texture coordinates within the range [0, 1].

  • Accessors/Buffer Views

    • glTF assets that have shared accessors and/or buffer views are supported. However, this is expanded when tiling which may require large amounts of virtual memory.

OBJ input requirements

  • .obj and .mtl files must use UTF-8 encoding.

  • Models must have vertices (v)

  • The input models may have n-gon faces, they will be triangulated in the output

  • Textured Models must have texture coordinates (vt)

  • Only the diffuse color texture (map_Kd) or diffuse factor (Kd) are supported

  • Only PNG and JPEG textures are currently supported.

  • All other vertex attributes (normals, vertex colors) are ignored.

  • out-of-bounds texcoords are assumed REPEAT texture wrapping. the -clamp flag is not supported.

  • If the OBJ references a file with spaces in the filename, the filename must be enclosed in quotes, e.g. "my mesh.obj" or the space must be escaped with a backslash, e.g. my\ mesh.obj.

3D Tiles 1.1 output

The Reality Tiler produces a 3D Tiles 1.1 tileset. It uses the following features of the specification:

  • glTF content

    • Each tile contains a single content in glTF format.

    • Each glTF asset consists of a single triangle mesh with a base color texture

    • The mesh’s material uses the KHR_materials_unlit extension, as reality models often look best without shading

    • Mesh primitives contain POSITION and TEXCOORD_0 attributes.

    • Future releases of the 3D Tiling Pipeline will preserve more attributes and metadata from the input model

  • Implicit tiling

    • The tileset is an octree and uses implicit tiling for efficient run-time queries by Morton index. Furthermore, only the root bounding volume needs to be stored, which keeps the tileset.json small.

  • Tile Metadata

    • TILE_BOUNDING_BOX - The root tile contains a metadata property with this semantic to store a tighter bounding box than the one used for implicit tiling. Runtime engines can use this bounding box to produce a better camera view when zooming to the tileset. See for example this CesiumJS PR.

Error codes list

Below is a list of all error codes supported in the 3D Tiling Pipeline. Each error consists of three parts: a unique numerical code, a "category", and a string description. There are three error categories:

  • USER - an issue with input data or incompatible CLI arguments

  • CONFIG - an issue with the provided config file, e.g. invalid options

  • INTERNAL - an internal processing error

Error Code Category Description




An input to the tiler (such as a command line flag) is invalid




One of the specified input files could not be found




One of the specified input files is invalid




One of the specified input files has unsupported feature.




The input file does not fit in availabile virtual memory.




A config file parameter is invalid




A config file parameter is out of the allowed range




A config file parameter is not a valid value

Additionally, many error types contain a reason field to provide more details. The list of reason codes is as follows:

Error reason Description


Failed to parse the config JSON file.


No config file, input or input list was provided.


A required command line option was omitted.


A provided command-line option has an argument that does not have the expected type and could not be parsed.


A provided command-line option does not have an argument but requires one.


A provided command-line option has an argument but does not take one.


A provided command-line option is not recognized.


There was an error parsing the CLI options.


The specified coordinate reference systems are incompatible. This can happen if one CRS is a local coordinate system and another is a global coordinate system.


Could not remove the specified directory.


The tilers executable was inside the output directory and would be overwritten.


The specified file could not be read.


The specified file could not be deleted.


The specified file could not be written.


The specified file is empty.


The scale factor provided with --scale-{x,y,z} was zero, which would lead to degenerate geometry.


A data URI for a glTF buffer is invalid.


The glTF model failed to load.


A warning was emitted when the glTF model was loaded.


The specified URI in the glTF is invalid, but should point to an image.


The specified URI in the glTF is invalid, but should point to a glTF buffer.


The input glTF file does not have any meshes.


The input glTF file does not have any primitive with a POSITION attribute.


The input glTF file does not have any primitive.


--input-crs must be set when using --input-crs-origin-{x,y,z}


The input contains a mix of different input file formats. Tilers currently requires all inputs to have the same format.


The input format could not be determined from the input file.


The input directory is inside the output directory and would be overwritten.


The specified path is not a valid file path.


The input directory is the same as the output directory and would be overwritten.


The file with input paths could not be read.


--input and --input-list are mutually exclusive.


The coordinate reference system was not valid. It must be a CRS supported by the PROJ library.


The input provided is incompatible with the selected pipeline.


The provided license was invalid.


The input model uses a format not supported by the 3D Tiling Pipeline.


An image from a model could not be decoded.


No valid input models could be read from the input.


No input path(s) were specified.


No output path was specififed.


The pipeline to use could not be determined from the input and output formats.


None of the input file(s) contain valid meshes for tiling.


An image from an OBJ file could not be decoded.


The input OBJ file does not contain any valid data.


The specified OBJ file failed to load.


A warning was emitted when loading the OBJ file.


The OBJ contains one or more invalid texture coordinate index.


The OBJ contains one or more invalid normal index.


The OBJ contains one or more invalid vertex position index.


The output directory already exists and would be overwritten. To force writing to this directory, use the --overwrite flag.


The temporary directory could not be found.


The tiler could not create the temporary directory.


Vertex colors are not yet supported.


glTF extension not currently supported.


Input model is too big, not enough virtual memory to load it.


glTF bufferViews sharing can currently require an excessive amount of virtual memory. If this is a CAD model, try the CAD tiler instead.


Model has triangles that span many copies of the texture. This would require excessive virtual memory to texture bake in terms of virtual memory. If this is a CAD model, try the CAD tiler instead.


A glTF buffer view extends past the end of the underlying buffer.


The glTF accessor extends past the end of the underlying buffer view.


The glTF references an accessor that does not exist.


The glTF references a buffer view that does not exist.


The glTF references a buffer that does not exist.


The glTF references an image that does not exist.


glTF primitive modes other than TRIANGLES (mode = 4) are not supported.