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

BVH: Triangles: 2166 (pre-transformed) BVH nodes: 4407 (2203 interior, 2204 leaves) Max prims: 1 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 anyhit 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)
MethodTime_μs
closest_hit
BenchmarkTools.Trial: 10000 samples with 10 evaluations per sample.
 Range (min … max):  1.209 μs …   3.783 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     1.227 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   1.241 μs ± 101.731 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▄█▄                                                         ▁
  ███▅▁▄▁▆▁▃▁▁▁▇█▃▄▄▄▃▁▁▁▁▃▃▁▁▁▁▁▁▁▁▃▃▁▁▃▅▃▁▁▁▁▁▁▁▁▁▁▁▁▃▁▃▄▆▇ █
  1.21 μs      Histogram: log(frequency) by time      1.96 μs <

 Memory estimate: 0 bytes, allocs estimate: 0.
any_hit
BenchmarkTools.Trial: 10000 samples with 4 evaluations per sample.
 Range (min … max):  7.341 μs …  26.371 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     7.391 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   7.489 μs ± 605.295 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

  ▆█▅                                                    ▁    ▁
  ████▆▅█▇▅▄▄▁▆▇▅▆▅▅▄▃▃▄▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▃▁▁▁▁▁▁▁▁▃▁▁▁▆████▇▆ █
  7.34 μs      Histogram: log(frequency) by time      9.44 μ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! ✓