How Point Size Works in glTF Point Clouds
This article examines how point size parameters operate when rendering point cloud primitives within the glTF 2.0 specification. While glTF inherently supports point cloud geometry through its primitive rendering modes, the core specification does not define a dedicated material property for point size. As a result, point sizing relies on a combination of lower-level graphics API mechanics, runtime vertex shaders, custom attributes, and viewer-specific implementations.
Primitive Mode 0: POINTS
In glTF, point clouds are defined inside the meshes
array using primitive objects where the mode property is
set to 0 (POINTS). Each index or vertex
position provided in the accessor array represents an individual point
in 3D space rather than a vertex of an interconnected polygon or
line.
While the format standardizes vertex attributes such as
POSITION, COLOR_0, and NORMAL, it
omits a standardized, core PBR material property for setting the visual
diameter or radius of these points.
The Role of WebGL and Graphics APIs
Because core glTF delegates primitive rasterization to underlying graphics APIs (such as WebGL, WebGPU, OpenGL, or Vulkan), point size behavior defaults to the API's rendering pipeline:
- Hardware Defaults: In standard WebGL, rendering
POINTSrelies on the vertex shader setting the special variablegl_PointSize. If the shader does not explicitly set or calculate this value, it defaults to1.0, producing single-pixel points regardless of camera distance or screen resolution. - Hardware Limitations: APIs enforce
implementation-dependent minimum and maximum point sizes. For example,
WebGL devices expose an
ALIASED_POINT_SIZE_RANGE. Points configured beyond these hardware limits are clamped, preventing extremely large splats unless rendered as quad instancing or triangle meshes instead of native point primitives.
Screen-Space vs. World-Space Attenuation
How point sizes scale relative to the camera depends entirely on how the runtime viewer’s shader pipeline handles attenuation:
- Fixed Screen-Space Sizing: The vertex shader
assigns a constant scalar value to
gl_PointSize. The points maintain an identical pixel size across the screen regardless of whether the camera moves closer or further away. - Perspective Attenuation (World-Space Sizing): The
vertex shader scales
gl_PointSizeinversely proportional to the camera distance (often calculated aspointSize = baseSize * (viewportHeight / -viewPosition.z)). This causes points to expand when viewed up close and shrink when viewed from a distance, simulating physical 3D volume.
Because glTF does not standardize distance attenuation formulas in its material schemas, different glTF viewers (such as Three.js, Babylon.js, or Cesium) may render the identical file with different visual densities unless explicitly overridden via client-side code.
Custom Vertex Attributes and Per-Point Sizing
To support variable point sizes within a single point cloud, creators often utilize custom application-specific vertex attributes. According to the glTF 2.0 specification, custom attributes must begin with an underscore:
_SIZEor_RADIUS: A floating-point scalar accessor assigned to each vertex.- Shader Integration: Viewers customized to support
these point clouds bind the custom accessor to the vertex shader,
computing
gl_PointSizedynamically per vertex rather than using a uniform value for the entire mesh primitive.
Handling Point Sizes in Production
Since point size is not strictly standardized in core glTF, production workflows typically adopt one of the following approaches:
- Engine Uniforms: Setting point size at the
application level through runtime engine materials (e.g., modifying
material.sizein Three.js ormaterial.pointSizein Babylon.js). - Instanced Meshes: Converting point clouds into
instanced geometry (such as low-poly spheres or camera-facing quads)
using the
EXT_mesh_gpu_instancingextension. This guarantees fully deterministic, physical world-space sizing across any compliant glTF viewer at the cost of higher primitive overhead.