dev-docs/RFCs/proposals/layer-culling-rfc.md
This RFC proposes adding the capability of layers to express bounding volumes, and to check those against the the current view (frustum) before GPU rendering, and avoid rendering layers that are not currently visible.
We need to implement frustum culling for 3D tiles which is a substantial effort and this RFC proposes generalizing the implementation so that all deck.gl layers and use cases can benefit from this standard 3D engine feature.
The ability to avoid rendering non-visible geometries is particularly important for large segmented data sets such as 3D Tile and potree point clouds, where hundreds of millions of points can be loaded into memory across hundreds of tiles.
When moving in first-person view inside big point clouds, the idea is to only rendering things withing the current view frustum. This can lead to substantial performance increase, often 3-4x depending on direction of view and how tight the view frustum is, which can turn 5 FPS into 20 FPS.
Culling requires intersecting bounding volumes with the view frustum, using a range of well known but not completely trivial 3D geometry techniques.
In our case, bounding volumes (spheres and boxes) need to be fully 4x4 transformable (which means that the simple axis aligned bounding box classes used by many 3D libraries are not sufficient), but instead we need a more complex OrientedBoundingBox.
The necessary math library (BoundingSphere, OrientedBoundingBox, CullingVolume, PerspectiveFrustum etc is being developed in the @loaders.gl/3d-tiles module, with the Cesium code base as reference.
The current idea is to eventually move these classes out from loaders.gl and publish them as submodules of math.gl:
import {OrientedBoundingBox, PerspectiveFrustum} from '@math.gl/culling';
These are very rough initial proposals, ideas are welcome!
Initially the app could provide the bounding volume as a prop.
import {OrientedBoundingBox} from '@math.gl/culling';
new Layer {
boundingVolume: new OrientedBoundingBox()
};
Setting a boundingBox would automatically cause it to be compared against the current view's view frustum and potentially culled.
The supplied bounding box would presumably be expressed in the layer's coordinate system (model matrix and coordinateSystem would be used to transform it).
Later we could have layers that can automatically calculate their bounding volumes from their data.
new Layer {
boundingVolume: 'auto'
};
Since bounding box calculations can be expensive we may not want to do them by default.
Any View must be able to generate a camera position and view frustum that can be used by the culling and layer prioritization algorithms.
Use case: The 3D Tile traversal algorithm will rank all tiles with a priority, which depends on e.g. the size of that tile in the current view, etc.
To keep interactivity high when half a billion points are loaded, as the layers corresponding to tiles are rendered in priority order, GPU time is consumed, and once the time budget has been consumed, rendering of less relevant tiles can stop.
Layers would have the ability to render a wireframe geometry of their bounding geometries to help debug culling.
We would need to define the coordinate system in which bounding volumnes and frustums should be expressed.
Unfortunately, the "common" coordinate system is scale dependent, we may want to define an "unscaled common" coordinate system for these calculations.
A number of Geospatial math classes such as Ellipsoid and Cartographic are being added to loaders.gl and could be moved to @math.gl/geospatial.
While any discrepancies are likely to be minor, gor complete correctness, the conversion of geospatial bounding regions in non-linear coordinates to linear coordinate regions may need additional thought.
If deck rerenders but viewport doesn't change, culling doesn't need to be recalculated.