BVH Hit tests

BVH Hit Testing: closest_hit vs any_hit

This document tests and visualizes the difference between closest_hit and any_hit functions in the BVH implementation using the new RayIntersectionSession API.

Test Setup

BVHAccel: Triangles: 2166 BVH nodes: 4407 (2203 interior, 2204 leaves) Bounds: Float32[-0.98299235, -0.99318063, 0.0] to Float32[0.9965845, 0.99318063, 6.0] Max prims: 1 per leaf Avg prims: 0.98 per leaf

Test 1: Single Ray Through Center

Test a ray through the center that passes through all three spheres.

Test 2: Multiple Rays from Different Positions

Test multiple rays to ensure both functions work correctly.

Visualization: Multiple Rays

Test 4: Difference Between any hit and closesthit

Demonstrate that any_hit can return different results than closest_hit.

Key Findings:

  • any_hit exits on the first intersection during BVH traversal (uses intersect, doesn't update ray)

  • closest_hit continues searching and updates ray's t_max (uses intersect_p!)

  • In complex scenes with overlapping geometry, any_hit can return hits that are significantly farther

  • Both always agree on whether a hit occurred (hit vs miss)

  • The difference appears when BVH traversal order differs from spatial distance order

Performance Comparison

Compare the performance of closest_hit vs any_hit.

render_io (generic function with 1 method)
Method Time_μs
closest_hit
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min … max):  10.920 μs …  29.164 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     11.001 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   11.105 μs ± 849.468 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  █                                                             
  █▅▂▂▂▂▂▂▂▁▂▂▂▂▂▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▂▂ ▂
  10.9 μs         Histogram: frequency by time         18.1 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.
any_hit
BenchmarkTools.Trial: 10000 samples with 1 evaluation per sample.
 Range (min … max):  14.326 μs …  35.617 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     14.427 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   14.566 μs ± 964.957 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  █▇▂                                                          ▁
  ███▆▅▅▇█▅▁▁▄▄▁▄▄▅▅▃▁▁▁▁▁▄▁▁▁▁▁▁▁▁▁▁▁▁▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▅▇▇ █
  14.3 μs       Histogram: log(frequency) by time      21.8 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.

Summary

This document demonstrated:

  1. RayIntersectionSession - A convenient struct for managing ray tracing sessions

    • Bundles rays, BVH, hit function, and results together

    • Provides helper functions: hit_count(), miss_count(), hit_points(), hit_distances()

  2. Makie visualization recipe - Automatic visualization via plot(session)

    • Automatically renders BVH geometry, rays, and hit points

    • Customizable colors, transparency, markers, and labels

    • Works with any Makie backend (GLMakie, WGLMakie, CairoMakie)

  3. closest_hit correctly identifies the nearest intersection among multiple overlapping primitives

    • Returns: (hit_found::Bool, hit_primitive::Triangle, distance::Float32, barycentric_coords::Point3f)

    • distance is the distance from ray origin to the hit point

    • Use Raycore.sum_mul(bary_coords, primitive.vertices) to convert to world-space hit point

  4. any_hit efficiently determines if any intersection exists, exiting early

    • Returns: Same format as closest_hit: (hit_found::Bool, hit_primitive::Triangle, distance::Float32, barycentric_coords::Point3f)

    • Can exit early on first hit found, making it faster for occlusion testing

  5. Both functions handle miss cases correctly (returning hit_found=false)

  6. any_hit is typically faster than closest_hit due to early termination

All tests passed! ✓