# How Voxelization Works in the rgbd_render Module Using CUDA

> Learn how GPU accelerated voxelization works in the rgbd_render module. Explore CUDA device sorting and deduplication strategies for efficient RGB-D scene processing.

- Repository: [Robbyant/lingbot-map](https://github.com/Robbyant/lingbot-map)
- Tags: how-to-guide
- Published: 2026-07-08

---

**The rgbd_render module performs GPU-accelerated voxelization of RGB-D scenes using a Morton-code sorting strategy that assigns points to 3D grid cells, sorts them by voxel index, and deduplicates colors entirely on the CUDA device.**

The lingbot-map repository's rgbd_render pipeline converts dense RGB-D point clouds into compact voxel representations using a fully GPU-accelerated workflow. This voxelization process in the rgbd_render module using CUDA eliminates CPU bottlenecks by handling index computation, spatial sorting, and deduplication within CUDA kernels. The implementation leverages Morton codes for efficient spatial hashing, enabling real-time processing of hundreds of thousands of points per frame.

## The Three-Stage Voxelization Architecture

The voxelization pipeline splits responsibilities across three tightly-coupled layers to maximize GPU utilization.

### High-Level Orchestration in SceneBuilder

In [`demo_render/rgbd_render/pipeline/builder.py`](https://github.com/Robbyant/lingbot-map/blob/main/demo_render/rgbd_render/pipeline/builder.py), the `SceneBuilder` class coordinates the end-to-end pipeline. After loading and preprocessing raw RGB-D frames, the builder calls `.voxelize()` to trigger the GPU conversion process. This method manages frame batching and delegates the actual voxel grid construction to the geometry layer.

### Python Wrapper Interface

The `VoxelGridCUDA` class in [`demo_render/rgbd_render/geometry/voxel.py`](https://github.com/Robbyant/lingbot-map/blob/main/demo_render/rgbd_render/geometry/voxel.py) serves as the Python façade for the CUDA backend. It accepts per-frame point clouds as GPU tensors (`xyz` and `rgb`) and forwards them through the `render_cuda_ext.voxelize_frame` helper. The wrapper handles memory management, optional deferred merging via the `defer_merge` flag, and final grid assembly when users call `voxel_grid()`.

### CUDA Kernel Implementation

The core computation resides in `demo_render/render_cuda_ext/voxel_morton/voxel_morton.cu`, exposed to Python via [`voxel_morton_bind.cpp`](https://github.com/Robbyant/lingbot-map/blob/main/voxel_morton_bind.cpp). The `voxelize_frame_cuda` kernel executes the voxelization entirely on the device, producing `voxel_centers` and `voxel_colors` tensors without CPU intervention.

## The CUDA Voxelization Algorithm

The kernel implements a classic *Morton-code + GPU-sort* strategy for spatial hashing and deduplication.

### Voxel Index Computation

Each point is assigned to a 3D grid cell by dividing its coordinates by the `voxel_size` and applying floor operations:

```cpp
int ix = (int)floorf(pts_xyz[3*i+0] / voxel_size);
int iy = (int)floorf(pts_xyz[3*i+1] / voxel_size);
int iz = (int)floorf(pts_xyz[3*i+2] / voxel_size);

```

The voxel center is calculated as `(ix + 0.5) * voxel_size` for each dimension, while the original RGB color is preserved for that point.

### Morton Encoding and Parallel Sorting

To group points belonging to the same spatial cell, the kernel computes a **Morton code** (Z-order curve index) from the 3D grid coordinates. This linearized spatial index serves as a sort key. Using `thrust`, the kernel performs a parallel sort that brings all points falling into identical voxels adjacent in memory.

### Deduplication and Color Aggregation

Following the sort, a second kernel pass identifies unique voxels by detecting changes in the sorted Morton codes. For each unique voxel, the system retains the first encountered color (or applies other aggregation strategies specified by the `color_update` parameter), yielding a compact set of unique voxel centers and colors.

## Python-Side Grid Management

After the CUDA kernel returns per-frame results, `VoxelGridCUDA._insert_tensors` manages the integration of new data into the global grid. When `defer_merge=True` (set during initialization via `create_voxel_grid`), the class accumulates frame tensors in a list and postpones the expensive merge operation until the user requests the final grid. This optimization reduces synchronization overhead during streaming processing.

## Implementation Examples

To create a voxel grid with specific parameters:

```python
from demo_render.rgbd_render.geometry import create_voxel_grid

# Initialize with 5mm voxels and first-seen color policy

voxel_grid = create_voxel_grid(
    voxel_size=0.005,           # 5 mm voxels

    color_update='first',       # keep the first colour encountered

    batch_size=20,              # process 20 frames per CUDA launch

    defer_merge=True            # postpone merging for speed

)

```

Inserting a single frame of point cloud data:

```python

# xyz and rgb are torch tensors on GPU with shape [N, 3]

voxel_grid.insert_frame(xyz, rgb, frame_idx=0)

```

Using the high-level pipeline builder:

```python
from demo_render.rgbd_render.pipeline.builder import SceneBuilder

scene = (
    SceneBuilder(cfg, log=print)   # cfg holds --voxel_size etc.

        .load()                    # reads RGB‑D frames

        .preprocess()              # filters, normalises

        .voxelize()                # runs the CUDA voxelisation

        .build()                   # returns a Scene object

)
print(f"[scene] {len(scene.sorted_xyz):,} voxels, {scene.num_frames} frames")

```

Accessing the final voxel grid for rendering or export:

```python
centers, colors = scene.voxel_grid()   # both are torch tensors (GPU)

# Pass directly to renderer or export to PLY/OBJ formats

```

## Summary

- The rgbd_render module implements **fully GPU-accelerated voxelization** using CUDA kernels to process hundreds of thousands of points per frame without CPU transfers.
- **Morton-code sorting** in `voxel_morton.cu` efficiently groups points into 3D grid cells using parallel thrust operations.
- The `VoxelGridCUDA` class in [`voxel.py`](https://github.com/Robbyant/lingbot-map/blob/main/voxel.py) provides a Python interface that supports **deferred merging** to optimize streaming performance.
- **SceneBuilder** orchestrates the pipeline from raw RGB-D frames to final voxel grids via [`demo_render/rgbd_render/pipeline/builder.py`](https://github.com/Robbyant/lingbot-map/blob/main/demo_render/rgbd_render/pipeline/builder.py).
- Color aggregation strategies like `'first'` are applied during the deduplication pass to handle multiple points falling within the same voxel.

## Frequently Asked Questions

### What is Morton coding in voxelization?

Morton coding converts 3D grid coordinates into a single linear index by interleaving the bits of each dimension. In the rgbd_render module, this Z-order curve encoding serves as the sort key that allows the CUDA kernel to group spatially adjacent points together, enabling efficient parallel deduplication without hash table lookups.

### How does the rgbd_render module handle color aggregation?

The module supports multiple color aggregation strategies controlled by the `color_update` parameter in `create_voxel_grid()`. When set to `'first'`, the kernel retains the color of the first point encountered in each voxel after sorting. Alternative modes can be implemented in the deduplication pass of `voxel_morton.cu` to average colors or select based on confidence values.

### Where is the CUDA kernel defined in the repository?

The primary CUDA kernel `voxelize_frame_cuda` is defined in `demo_render/render_cuda_ext/voxel_morton/voxel_morton.cu`. It is exposed to Python through the binding code in [`demo_render/render_cuda_ext/voxel_morton/voxel_morton_bind.cpp`](https://github.com/Robbyant/lingbot-map/blob/main/demo_render/render_cuda_ext/voxel_morton/voxel_morton_bind.cpp), which creates the `render_cuda_ext` module callable from `VoxelGridCUDA`.

### Can I adjust the voxel size for different scene scales?

Yes, the voxel size is configurable via the `voxel_size` parameter (in meters) when calling `create_voxel_grid()` or through the configuration passed to `SceneBuilder`. Smaller values (e.g., 0.01 for 1cm) increase resolution but require more memory and computation, while larger values create coarser representations suitable for distant geometry.