3dtiled-to-3dtiles
Reference notes

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 out

Tip: 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

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


Gaussian-splat tilers (→ LoD trees of splats)

ToolOutputNotes
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 treeVariable 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-Converter3D Tiles of glb splatsPLY → 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); see lcc-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:

  1. 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.
  2. 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.
  3. 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.copc is 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), not writers.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-tiler are 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.copc is 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.

On this page