ComputeBatchCulling

@three-blocks/coreWebGPUr181
new ComputeBatchCulling(options : ComputeBatchCullingOptions)

GPU-driven culling for IndirectBatchedMesh with matrix-based reference positions.

Architecture

  • Optimized for IndirectBatchedMesh with per-instance matrices
  • Two-pass block compaction algorithm for efficient parallel writes
  • Supports both ref-position and matrix-based culling
  • Per-geometry bounding sphere tests (reads from mesh geometry info)
  • Writes survivor IDs for indirect instanced draw

Limitations

  • Transparency is not supported: IndirectBatchedMesh does not support transparent materials. Use opaque materials only. Depth sorting is not available.

Block Compaction Algorithm

  1. Count Pass: Each instance atomically increments its block counter if visible
  2. Prefix Sum: CPU computes block offsets (serial over blocks, parallel within)
  3. Scatter Pass: Each visible instance writes to its block's allocated slot

Matrix vs Position Mode

  • Position Mode (default): Uses refPosition/refNormal arrays
  • Matrix Mode (via bindMatricesAsReference()): Extracts position from matrices
    • Enables per-geometry bounding sphere culling
    • Ideal for IndirectBatchedMesh integration

LOD System

  • Purpose: Reduce draw density smoothly with distance by keeping each instance with probability pKeep.
  • Near/Far: lodNear defines where LOD begins. lodFar defines where range mode reaches full falloff.
  • Modes (lodMode):
    • Disabled (LOD_MODE_DISABLED): LOD sampling is off.
    • Range (LOD_MODE_RANGE): Smoothstep falloff between lodNear and lodFar. pKeep = 1 - smoothstep( lodNear, lodFar, d )
    • Exp (LOD_MODE_EXP): Exponential density falloff starting at lodNear. pKeep = exp( -density^2 * (d - lodNear)^2 ) Note: lodNear shifts the start of the exp curve; increasing it delays the falloff.
  • Density Parameter: lodDensity acts as exp density (smaller = slower decay).
  • Sampling: Each instance is kept if hash(instanceId) < pKeep, giving stable stochastic thinning.
  • Compatibility: Internally computes stepF = sqrt(1 / max(pKeep, eps)) for legacy outputs.

Integration with IndirectBatchedMesh

Constructor Parameters
optionsComputeBatchCullingOptions
Configuration options.
See also
Example
import { IndirectBatchedMesh, ComputeBatchCulling, indirectBatch } from '@three-blocks/core';

// Create batched mesh
const count = 10000;
const mesh = new IndirectBatchedMesh(count, 50000, 150000, material);
mesh.frustumCulled = false; // Disable CPU culling

// Add geometries and instances
const sphereId = mesh.addGeometry(new SphereGeometry(1, 16, 16));
for (let i = 0; i < count; i++) {
  const id = mesh.addInstance(sphereId);
  const matrix = new Matrix4();
  matrix.makeTranslation(
    Math.random() * 100 - 50,
    Math.random() * 100 - 50,
    Math.random() * 100 - 50
  );
  mesh.setMatrixAt(id, matrix);
}

// Setup material with indirect batch node
material.positionNode = Fn(() => {
  indirectBatch(mesh).toStack();
  return positionLocal;
})();

// Create culler
const culler = new ComputeBatchCulling({
  renderer,
  count,
  indexCount: sphereGeometry.index.count,
  enabled: true
});

// Bind mesh matrices as culling reference
culler.bindMatricesAsReference(mesh);
culler.attachGeometry(mesh.geometry);
culler.attachMesh(mesh);

// Render loop
function animate() {
  culler.setCameraUniforms(camera);
  culler.update();  // GPU culling
  renderer.render(scene, camera);
}

Properties

.indirect : THREE.IndirectStorageBufferAttribute

Get indirect draw buffer.

.outIdSSBO : THREE.StorageBufferAttribute

Get survivor ID buffer.

.outIdNode : StorageNode

Get survivor ID storage node (TSL).

Methods

setCameraUniforms#

setCameraUniforms(camera : THREE.Camera)

Update camera uniforms for frustum culling. Call before update() each frame.

Parameters
cameraTHREE.Camera
Active camera for culling tests.

attachGUI#

attachGUI(folder : Object)

Attach culling controls to a GUI folder. Compatible with lil-gui, dat.gui, and Three.js Inspector.

Parameters
folderObject
GUI folder instance (e.g., from lil-gui or renderer.inspector.createParameters()).

disposeGUI#

disposeGUI()

Detach and destroy the GUI folder.

update#

update()

Execute GPU culling with two-pass block compaction. Must be called every frame before rendering.

attachGeometry#

attachGeometry(geometry : THREE.BufferGeometry)

Attach geometry to receive indirect draw args.

Parameters
geometryTHREE.BufferGeometry
Target geometry for indirect rendering.

attachMesh#

attachMesh(mesh : THREE.Mesh)

Attach mesh and configure survivor buffer.

Parameters
meshTHREE.Mesh
Target mesh instance.

setReferenceMatrixNode#

setReferenceMatrixNode(resolverFn : function|null) : this

Override the matrix used for culling (e.g., animation texture).

Parameters
resolverFnfunction | null
Function receiving { i, baseMatrix, gid } and returning a mat4 node.
Returns
this

bindMatricesAsReference#

bindMatricesAsReference(mesh : IndirectBatchedMesh)

Bind mesh matrices as culling reference (enables matrix mode). Extracts positions from instance matrices and enables per-geometry sphere tests. Automatically called by IndirectBatchedMesh when culling is enabled.

Parameters
Mesh with matricesSB storage buffer.

readIndirectArgs#

readIndirectArgs() : Promise<Uint32Array>

Read back indirect draw arguments from GPU (debug/stats).

Returns
Promise<Uint32Array> — Array of [indexCount, instanceCount, firstIndex, baseVertex, firstInstance].

readSurvivorIndicesAsync#

readSurvivorIndicesAsync() : Promise<Uint32Array>

Read back surviving instance IDs from GPU (debug/analysis).

Returns
Promise<Uint32Array> — Array of survivor instance indices.

dispose#

dispose()

Dispose of GPU resources.