Offline tilers — building tiled hierarchies (point clouds, splats, meshes)
Reference catalogue of offline tilers that produce the tiled inputs this project converts to (or
Offline tilers — building tiled hierarchies (point clouds, splats, meshes)
Reference catalogue of offline tilers that produce the tiled inputs this project converts to (or serves as) OGC 3D Tiles. The converters in this repo consume the outputs of these tools (COPC, Potree, streamed-SOG, RAD, I3S); this page records how those inputs are produced.
Point-cloud tilers
Potree (PotreeConverter)
Intensity must be 0–65535 and named Intensity in the LAS (e.g. CloudCompare LAS 1.2 v2 export).
# Standard: projection as UTM string
PotreeConverter.exe pointcloud.las -o cloud_Potree -p index --projection "+proj=utm +zone=31 +datum=WGS84 +units=m +no_defs "
# Set spacing
PotreeConverter.exe scan.las -p index -o out --scale 0.0001 --overwrite
# Include normals (from ascii or ply)
PotreeConverter.exe ptc.xyz -f xyzrgbXYZ -a RGB NORMAL -o out -p index --overwrite
PotreeConverter.exe ptc.ply -a RGB NORMAL -o out -p index
# Include Intensity in output attributes
PotreeConverter.exe cloud.las --output-attributes RGB INTENSITY --intensity-range 0 65536 --material INTENSITY --overwrite -p cloud -o outTip: store any per-point scalar field (e.g. a CloudCompare C2C distance) in the LAS Intensity field
(2-byte int, 65536 values) to display it in Potree's intensity mode. Rename the scalar field to
Intensity before LAS export. (Older CloudCompare LAS 1.4 export had a min/max-xyz=0 header bug — use CC 2.13+.)
Building OGC/Cesium 3D Tiles from point clouds
- py3dtiles (repo) —
py3dtiles convert cloud.las --out /tmp/dst; supports tilesetmerge; docker images; mesh tiling planned (v7 notes). - gocesiumtiler — fast Go LAS → 3D Tiles (1.0/1.1).
gocesiumtiler-win-x64.exe file -out ./out-1.1 -version 1.1 pointcloud.1.4.las gocesiumtiler-win-x64.exe file -version 1.1 -o Exports/$m -e EPSG:32648 --resolution 1 --subsample 1 $m.las - potree23dtiles — Potree → 3D Tiles.
- ept-tools — LAS → EPT → 3D Tiles.
- Gaia3D/mago-3d-tiler — point clouds + meshes → 3D Tiles.
- Terrain: Gaia3D/mago-3d-terrainer or cesium-terrain-builder-docker (quantized-mesh from GeoTIFF).
- Older (reference only): entium, cesium.entwine.io, mattshax/cesium_pnt_generator, tum-gis/cesium-point-cloud-generator.
Building COPC via PDAL
COPC (Cloud-Optimized Point Cloud) replaces EPT; produced/consumed by
PDAL, pdal-wrench, untwine.
This repo streams COPC directly (/stream?format=copc).
[ "input.las", { "type": "writers.copc", "filename": "output.copc.laz" } ]Pipelines / dashboards
- geosolutions-it/digital-twin-toolbox — self-hosted point-cloud / shapefile 3D-tiling pipeline dashboard aggregating several tools.
Gaussian-splat tilers (→ LoD trees of splats)
| Tool | Output | Notes |
|---|---|---|
| PlayCanvas splat-transform (SuperSplat) | streamed-SOG (lod-meta.json + per-chunk WebP) | Binary spatial tree (AABB split), per-leaf LOD runs. Consumed by sog-to-3dtiles / /stream?format=sog. |
World Labs SparkJS build-lod (BhattLod) | .RAD LoD merge tree | Variable fanout (~1.5–1.75), downsampled merged representative splats per node. Consumed by rad-to-3dtiles / /stream?format=rad. Blog: Spark 2.0 — spatially partitioned LoD. |
| William Liu — 3DGS-PLY-3DTiles-Converter | 3D Tiles of glb splats | PLY → 3D Tiles directly (the geometric-error model our RAD tileset builder mirrors). |
LCC (XGRIDS Lixel CyberColor) is intentionally not listed here: there is no open-source tiler that produces
.lcc— the format is generated by XGRIDS' proprietary Lixel software/devices. Only the whitepaper spec and a reader (LCC-Web-SDK) are public. This repo reads.lcc(lcc-to-3dtiles//stream?format=lcc); seelcc-format.md.
Why is PotreeConverter 2.0 so fast?
PotreeConverter 2.0 (repo) is largely I/O- and counting-bound, not GPU-bound — it runs on the CPU. Its speed comes from a 3-pass, counting-sort-style build that avoids per-point tree descent:
- Counting pass — stream all points once, compute a fine regular grid (Morton/octant index from quantized coordinates), and histogram how many points fall in each cell. A flat-array increment per point — cache-friendly, trivially parallel over chunks.
- Allocate — from the counts, decide the octree node layout up front (which cells become nodes, their byte offsets in the output) so there is no dynamic tree growth during the heavy pass.
- Distribution pass — stream the points again and scatter each into its node's pre-allocated slot by its precomputed Morton index. Nodes over a target point budget spill the excess down to children; coarse levels keep a Poisson-disk / subsample so every level is renderable.
The wins vs. insert-one-point-at-a-time octrees: two linear streaming passes with sequential disk reads (no random tree-walk per point); Morton-code bucketing turns spatial placement into integer math; batch parallelism over fixed chunks with a final merge; the layout is known before writing, so output is one large sequential write. It's a radix/counting sort on Morton codes, not a comparison tree build — hence near-linear scaling that saturates disk bandwidth.
Could a GPU make it faster? (and the RAM→VRAM question)
For the binning/sort math, yes — GPUs do Morton-code radix sorts extremely well (this is exactly what GPU point/particle sorters and GPU BVH builders do). But for a whole-dataset out-of-core tiler (billions of points that don't fit in RAM, let alone VRAM), the bottleneck is not the arithmetic — it's moving bytes: disk → RAM → (PCIe) → VRAM → back. So:
- PCIe transfer dominates. A 10 B-point cloud at ~16 B/point ≈ 160 GB. You can't hold it in VRAM (24–80 GB), so you'd stream chunks host→device, sort/bin on GPU, read back, and merge — the PCIe round-trips (tens of GB/s) plus disk (a few GB/s NVMe) set the wall-clock. The GPU sort finishes long before the next chunk arrives → you're transfer-bound, and the GPU mostly idles.
- Where GPU genuinely helps: when the working set does fit in VRAM (≤ a few hundred M points), or for the per-chunk sort/bin/decimate kernels inside an otherwise CPU-orchestrated out-of-core pipeline (overlap copy with compute via double-buffering + CUDA streams). Then the GPU radix sort + Poisson-disk subsample per chunk can beat the CPU by a wide margin.
- Net: for 10 B points, a GPU won't give a 10× end-to-end win because RAM↔VRAM + disk dominate; it can still help the per-chunk hot loops if you keep data resident and hide transfers. The biggest lever remains fast sequential I/O + counting sort + chunk parallelism — exactly Potree 2.0's design.
Is COPC building the same as Potree 2.0? (corrected — they are NOT equally fast)
I over-simplified earlier. The right framing: the output container differs (COPC = one indexed LAZ with an EPT-style octree + hierarchy page; Potree = a directory of node files), but more importantly the implementations differ enormously in speed, and "PDAL builds COPC" hides which engine does it:
- PotreeConverter 2.0 is a purpose-built, heavily-parallel out-of-core tiler (the counting-sort / Morton-histogram + pre-allocate + scatter-with-spill described above). It's about the fastest thing there is for LAS/LAZ → octree, and saturates disk bandwidth.
- PDAL
writers.copcis part of PDAL's general pipeline engine. It is not the same optimized out-of-core counting-sort builder — it's a more general, less parallel writer, and in practice users report it is much slower than PotreeConverter 2.0 (matching your experience). PDAL's strength is generality (filters, reprojection, formats), not raw octree-build throughput. - untwine (hobuinc) is the fast, dedicated COPC builder — a two-phase "bin points into temp
files per cell, then assemble" out-of-core approach, multi-threaded, designed to bound memory on huge
clouds. If you want fast COPC, reach for
untwine(PDAL can shell out to it), notwriters.copc. - entwine (connormanning) builds EPT (not COPC) with a similar out-of-core spatial-binning design; mature and reasonably fast, but EPT is a directory of many files rather than one COPC LAZ.
So: the algorithm family (spatial bin → chunked write) is shared, but only some implementations are
counting-sort-fast. PotreeConverter 2.0 and untwine are in that tier; pdal writers.copc is not.
⚠️ The notes below on
py3dtiles/gocesiumtiler/mago-3d-tilerare best-effort from general knowledge, not benchmarked here — treat as hypotheses to verify:
- py3dtiles (Python) — convenient, supports tileset merge & docker; Python + multiprocessing makes it generally slower than the C++/Go tilers on very large clouds (GIL/serialization overhead), though fine for moderate sizes.
- gocesiumtiler (Go) — concurrent (goroutines), typically fast and a good middle ground; compiled, parallel, simple LAS→3D Tiles. Often the pragmatic fast choice for 3D Tiles output.
- mago-3d-tiler (Java) — broad input support (point clouds + meshes); JVM, multi-threaded; performance is decent but I have no measured comparison vs PotreeConverter 2.0.
Bottom line you can rely on: for raw octree-build speed, PotreeConverter 2.0 (Potree output) and untwine (COPC output) are the proven-fast tier;
pdal writers.copcis the slow one you hit.
Toward an out-of-core LAS/LAZ → 3D Tiles / COPC / Potree tiler (note)
A future fast tiler in this repo would reuse exactly this methodology: (1) stream LAS/LAZ once to histogram a Morton grid; (2) pre-allocate the octree layout + per-node byte offsets; (3) stream again, scatter to per-node temp buffers, spill over-quota points to children, Poisson-subsample coarse levels; (4) emit COPC (one indexed LAZ), Potree (node files), or 3D Tiles (pnts/glb) from the same in-memory node table. Bound memory with per-cell temp files (untwine-style) for clouds larger than RAM; optionally offload the per-chunk sort/decimate to the GPU when the chunk fits in VRAM.