Skip to content

Sobol' Quasi-Random Sampling

RayON uses a Sobol' low-discrepancy sampler as its default GPU sampler for both the CUDA and OptiX backends. Unlike pseudo-random generators (PCG), Sobol' sequences are constructed to fill space uniformly by design, giving significantly faster convergence of the Monte Carlo integral.


Why quasi-random?

Path tracing is fundamentally a Monte Carlo integration problem. Each pixel colour is the result of averaging many light transport samples:

\[ L \approx \frac{1}{N} \sum_{i=1}^{N} f(\mathbf{x}_i), \quad \mathbf{x}_i \sim p(\mathbf{x}) \]

The error of a Monte Carlo estimator based on pseudo-random samples shrinks as \(O(N^{-1/2})\). A quasi-random (low-discrepancy) sequence like Sobol' achieves \(O\!\left(\frac{(\log N)^d}{N}\right)\), which in practice means the same quality at a fraction of the samples.

Convergence plot comparing PCG and Sobol' error versus sample count on a log-log scale
Convergence comparison — integrating \(\sin(\pi x)\sin(\pi y)\) over \([0,1]^2\). Sobol' (orange) decreases error much faster than PCG (blue), especially beyond 64 samples.

PCG vs Sobol' — sample distribution

The key insight is visible in a 2D scatter plot of the first 256 samples:

2D scatter plots comparing PCG cluster/gaps with uniform Sobol' coverage
PCG (left) shows the characteristic clusters and gap regions of pseudo-random sampling. Sobol' (right) places points so that every sub-region of the unit square receives a proportional share — this is the low-discrepancy property.

Implementation details

Direction vectors

Sobol' sequences are generated by XOR-ing pre-computed direction vectors \(v_1, v_2, \ldots, v_{32}\) with the bits of the sample index. RayON uses the 128-dimension Joe–Kuo 2010 table (new-joe-kuo-6.21201) stored in 16 KB of CUDA constant memory (sobol_sampler.cuh):

__constant__ uint32_t sobol_directions[128][32];

The dimension layout in RayON is:

Dimensions Used for
0–1 Pixel anti-aliasing jitter (x, y)
2–3 Depth-of-field lens disc (u, v)
4–5 First-bounce material scatter direction (θ, φ)
6–7 Russian roulette + second-bounce material
8+ Subsequent bounces (2 dims per bounce level)
≥ 128 Falls back to PCG automatically

Gray-code ordering

Rather than evaluating samples in sequential order \(0, 1, 2, \ldots\), RayON uses Gray-code ordering:

\[ g(n) = n \oplus \lfloor n/2 \rfloor \]

Consecutive Gray-code values differ by exactly one bit, so each new sample only requires one XOR with a direction vector — making progressive (interactive/accumulative) rendering very cache-friendly.

Owen scrambling

Unscrambled Sobol' sequences show visible structure in 2D (known as "stratification artefacts"). RayON applies Laine–Karras Owen scrambling — a hash-based reversible bit permutation that:

  1. Preserves the low-discrepancy property (each scrambled sequence is still a valid \((0,2)\)-net)
  2. Is different per pixel (based on the pixel coordinate hash)
  3. Requires no extra memory — computed on-the-fly from a 32-bit seed
__device__ __forceinline__ uint32_t owen_scramble(uint32_t x, uint32_t seed)
{
    x = reverse_bits32(x);
    x ^= x * 0x3D20ADEAu;
    x += seed;
    x *= (seed >> 16u) | 1u;
    x ^= x * 0x05526C56u;
    x ^= x * 0x53A22864u;
    return reverse_bits32(x);
}

The per-pixel hash feeds a different scramble seed per dimension:

uint32_t dim_seed = pixel_hash ^ (dim * 0x9E3779B9u);

This ensures that neighbouring pixels produce decorrelated sample sequences, which directly translates to reduced structured noise in the final image.

Four 2D scatter plots of Sobol samples for four different pixels showing decorrelated distributions
Scramble diversity — four adjacent pixels use the same underlying Sobol' base sequence but receive fully independent scrambles, eliminating correlated noise patterns.

Per-dimension stratification

Each dimension used in path tracing is well-stratified independently. This matters because dimension 0 (AA jitter x) must be uniform even when projected onto the x-axis alone:

1D projections of five Sobol' dimensions across 64 samples showing even coverage
1D projections of the first 64 Sobol' samples across five dimensions. Each projects evenly onto [0,1], regardless of which dimension is examined.

GPU integration

CUDA path

Sobol' state is stored in the 48-byte curandState slot already allocated per-thread — no additional GPU memory is needed. The struct overlaid on the unused bytes is:

struct SobolSamplerState {
    uint32_t pixel_hash;   // Laine-Karras hash of (pixel_x, pixel_y)
    uint32_t sample_idx;   // Gray-code index for this sample
    uint32_t dim_idx;      // Next dimension to consume
    uint32_t pcg_seed;     // Fallback seed when dim_idx >= SOBOL_MAX_DIM
};

rand_float() in cuda_utils.cuh branches at runtime on the g_use_sobol device flag — no kernel re-compilation needed when switching samplers.

OptiX path

The same direction vectors and Owen scrambling are compiled into the PTX ray-generation program (optix_programs.cu). The sampler state is carried per-ray in the PRD (payload):

// In PRDRadiance (optix_params.h)
uint32_t sobol_sample_idx;
uint32_t sobol_dim_idx;
uint32_t sobol_pixel_hash;

The per-launch use_sobol boolean (set via OptixLaunchParams) selects Sobol' or PCG without pipeline rebuilds.


Choosing a sampler

# Default (Sobol' — recommended)
./rayon -m 2 -s 512 --scene resources/scenes/default_scene.yaml

# Classic pseudo-random PCG (for comparison or debugging)
./rayon -m 2 -s 512 --scene resources/scenes/default_scene.yaml --sampler pcg
Flag Sampler Convergence Notes
(default) or --sampler sobol Sobol' \(O\!\left(\frac{(\log N)^d}{N}\right)\) Best quality; recommended
--sampler pcg PCG \(O(N^{-1/2})\) Fallback; unstructured noise

Both samplers are available on CUDA (-m 2, -m 3) and OptiX (-m 4, -m 5) backends.


Visual comparison

The renders below isolate the effect of switching from PCG to Sobol' while keeping MIS disabled — so all noise differences come purely from the sampler choice. Drag the slider to compare.

Caustics chapel (512 SPP)

PCG Sobol' Caustics — PCG, no MIS Caustics — Sobol', no MIS

Left: PCG  |  Right: Sobol' — MIS off, 512 SPP. Sobol' reduces the high-frequency grain on the walls and caustic floor patterns.

Color bleed box (64 SPP)

PCG Sobol' Color Bleed — PCG, no MIS Color Bleed — Sobol', no MIS

Left: PCG  |  Right: Sobol' — MIS off, 64 SPP. The Cornell-box walls and soft colour gradients converge faster with Sobol'.

See the full comparison

Four scenes across all configurations are on the Visual Comparisons page.


References

  1. I. Joe and F. Kuo, "Constructing Sobol Sequences with Better Two-Dimensional Projections," SIAM J. Sci. Comput. 30 (2008) 2635–2654
  2. S. Laine and T. Karras, "Stratified Sampling for Stochastic Transparency," Eurographics (2011)
  3. B. Burley, "Practical Hash-based Owen Scrambling," JCGT Vol. 9 No. 4 (2020)