@bitruvius/ptcloud
v0.3.1
Published
Bitruvius point-cloud renderer: RGB/intensity/classification LiDAR rendering on WebGL2
Downloads
318
Readme
@bitruvius/ptcloud
The WebGL2 renderer behind every Bitruvius LiDAR layer.
Internal building block. This package exists so that
@bitruvius/sdk-maplibreand the Bitruvius codecs can resolve their dependencies on npm. It has no standalone product story. Unless you are deliberately building against it, install the SDK instead.
Why it is its own package
Point clouds reach Bitruvius from several directions: I3S point scene layers, 3D Tiles point
content (.pnts and glTF POINTS), standalone LEPCC captures, BVC scenes. Each has its own
codec, each runs off the main thread, and none of them knows anything about drawing.
They converge on one shape: the quantized tile contract in @bitruvius/geo-core
(DecodedPointTile for a single cloud, PackedPointTile for a streamed one). Positions on an
integer grid, plus optional RGB, intensity and LAS classification. This package is the other
side of that contract: it is what draws those tiles as a cloud. (@bitruvius/point-symbols
reads the same packed tiles, but draws icons and labels from them rather than points.)
That is the whole reason for the boundary. A codec that emits the contract gets rendering for free and never links against a renderer. The renderer never links against a codec, so a new format costs it nothing. Neither side has to be rebuilt when the other changes.
It is viewer-agnostic for the same reason. PointCloudRenderer implements
Renderer<DecodedPointTile> and is driven through a ViewerAdapter;
StreamingPointCloudRenderer takes a raw WebGL2RenderingContext and per-frame matrices in
the mercator world frame. Neither imports MapLibre or any other viewer. The only dependencies
are @bitruvius/geo-core (contracts and matrix math) and @bitruvius/render-runtime (GL
helpers and the shared clip, drape and shadow GLSL).
What is in it
Both renderers draw opaque gl.POINTS and dequantize in the vertex shader as
world = q * scale + offset, so positions stay compact integers all the way to the GPU.
Opaque means no depth sort: the depth buffer resolves occlusion, which is why the streaming
path needs no sort worker and no slot allocator, unlike the splat renderer.
PointCloudRendererdraws one static cloud, uploaded once.StreamingPointCloudRendererdraws the tiles an LOD engine has selected, each its own VAO with its own dequant offset, uploaded and released as the camera moves. It adds geometric-error distance attenuation, eye-dome lighting (a screen-space pass that shades depth gaps so a sparse cloud reads as a solid surface), layer opacity, clip planes and terrain drape.
Both cast and receive cascaded sun shadows as world-sized depth discs when a shadow
coordinator drives them, and both stay byte-identical until one does. Both expose an
on-demand GPU pick pass, and a top-down id grid (sampleDepthGrid) that the measure and
elevation-profile tools read as a DSM.
Symbology travels with them:
- Four color modes:
rgb,intensity,classificationandheight. - The Turbo ramp (Google's perceptual colormap, used under Apache-2.0) and the ASPRS class
palette are defaults, not fixtures. Turbo is a good ramp for LiDAR intensity and a poor one
for a color-blind reader, and a site's own class scheme routinely contradicts ASPRS.
So
buildRampLuttakes your stops andbuildClassColorLuttakes per-code overrides. Both produce the same 256-entry RGBA8 texture the shaders already sample, which is why changing symbology is atexSubImage2Dand never a re-decode. getClassificationLabelnames LAS codes 0 to 22 from the ASPRS spec and falls back toClass {n}for the reserved and user-defined range, so a UI can label whatever a dataset turns out to contain.perspectivePointSizePxis the unit-tested CPU reference for the shader's2 * r * focalPx / |z|sizing, alongside the helper that derivesfocalPxfrom the projection matrix.
import { PointCloudRenderer } from '@bitruvius/ptcloud';
const renderer = new PointCloudRenderer({ colorMode: 'intensity' });
renderer.init(adapter); // adapter.gl is a WebGL2RenderingContext
renderer.setQuantization(tile.scale, tile.offset);
const handle = renderer.upload(decodedTile);
// per frame:
renderer.render(frameState);Who should depend on it
If you are building an application, install
@bitruvius/sdk-maplibre. It wires
these renderers into map layers and re-exports PointCloudOptions and the color modes, so you
can tune point size, ramps and class visibility without importing this package at all.
Depend on @bitruvius/ptcloud directly only if you are hosting the renderers yourself, in
your own viewer or render loop, feeding tiles you have already packed into the
@bitruvius/geo-core contract. It versions in lockstep with the SDK, and its surface moves
with the SDK's needs.
Trademarks
Esri, I3S and LEPCC are trademarks of Environmental Systems Research Institute, Inc. 3D Tiles is a trademark of Cesium GS, Inc. glTF is a trademark of The Khronos Group Inc. Google is a trademark of Google LLC. MapLibre is a trademark of the MapLibre organization. All other marks are the property of their respective owners.
These names are used solely to describe the data formats this software interoperates with. Bitruvius is not affiliated with, sponsored by, or endorsed by any of them, and no such relationship is implied.
License
Proprietary. The full terms ship as LICENSE inside this package, and are readable
before installing at cdn.bitruvius.com/legal/sdk-license-v1.txt.
© Bitruvius, Inc.
