Ray Intersection
High-Level Functions
AsteroidShapeModels.intersect_ray_shape — Functionintersect_ray_shape(shape::ShapeModel, origins::AbstractMatrix{<:Real}, directions::AbstractMatrix{<:Real}) -> Vector{RayShapeIntersectionResult}Perform batch ray-shape intersection tests using the same interface as ImplicitBVH.traverse_rays.
This is the core implementation that all other intersect_ray_shape methods delegate to.
As of v0.4.0, BVH must be pre-built before calling this function. Use either with_bvh=true when loading or call build_bvh!(shape) explicitly.
Arguments
shape: Shape model (must have BVH built viabuild_bvh!)origins: 3×N matrix where each column is a ray origindirections: 3×N matrix where each column is a ray direction
Returns
- Vector of
RayShapeIntersectionResultobjects, one for each input ray
Throws
ArgumentErrorif BVH is not built. Callbuild_bvh!(shape)before using this function.
Notes
- This function provides a convenient interface that matches
ImplicitBVH.traverse_raysparameters - BVH must be pre-built using
build_bvh!(shape)or by creating the shape withwith_bvh=true - All rays are processed in a single batch for efficiency
Example
# Create ray data
n_rays = 100
origins = rand(3, n_rays) .* 1000 # Random origins
directions = normalize.(eachcol(rand(3, n_rays) .- 0.5)) # Random directions
# Convert directions back to matrix
directions = hcat(directions...)
# Perform batch intersection
results = intersect_ray_shape(shape, origins, directions)intersect_ray_shape(ray::Ray, shape::ShapeModel) -> RayShapeIntersectionResultPerform ray-shape intersection test using BVH acceleration. Uses the Möller–Trumbore algorithm for ray-triangle mesh intersection.
As of v0.4.0, BVH must be pre-built before calling this function. Use either with_bvh=true when loading or call build_bvh!(shape) explicitly.
Arguments
ray: Ray with origin and directionshape: Shape model (must have BVH built viabuild_bvh!)
Returns
RayShapeIntersectionResultobject containing the intersection test result
Throws
ArgumentErrorif BVH is not built. Callbuild_bvh!(shape)before using this function.
Notes
- BVH must be pre-built using
build_bvh!(shape)or by creating the shape withwith_bvh=true - To pre-build BVH, use
load_shape_obj("path/to/shape.obj"; with_bvh=true) - Or for an existing
ShapeModel:build_bvh!(shape)
Example
ray = Ray(SA[0.0, 0.0, 1000.0], SA[0.0, 0.0, -1.0])
result = intersect_ray_shape(ray, shape)
if result.hit
println("Hit face $(result.face_idx) at distance $(result.distance)")
endintersect_ray_shape(rays::AbstractVector{Ray}, shape::ShapeModel) -> Vector{RayShapeIntersectionResult}Perform batch ray-shape intersection tests for multiple rays.
As of v0.4.0, BVH must be pre-built before calling this function. Use either with_bvh=true when loading or call build_bvh!(shape) explicitly.
Arguments
rays: Vector of Ray objectsshape: Shape model (must have BVH built viabuild_bvh!)
Returns
- Vector of
RayShapeIntersectionResultobjects, one for each input ray
Throws
ArgumentErrorif BVH is not built. Callbuild_bvh!(shape)before using this function.
Example
# Create a vector of rays
rays = [Ray(SA[x, 0.0, 1000.0], SA[0.0, 0.0, -1.0]) for x in -500:100:500]
results = intersect_ray_shape(rays, shape)
# Count hits
n_hits = count(r -> r.hit, results)
println("$n_hits out of $(length(rays)) rays hit the shape")intersect_ray_shape(rays::AbstractMatrix{Ray}, shape::ShapeModel) -> Matrix{RayShapeIntersectionResult}Perform batch ray-shape intersection tests for a matrix of rays. The output shape matches the input shape, preserving spatial arrangement.
As of v0.4.0, BVH must be pre-built before calling this function. Use either with_bvh=true when loading or call build_bvh!(shape) explicitly.
Arguments
rays: Matrix of Ray objectsshape: Shape model (must have BVH built viabuild_bvh!)
Returns
- Matrix of
RayShapeIntersectionResultobjects with the same size as input
Throws
ArgumentErrorif BVH is not built. Callbuild_bvh!(shape)before using this function.
Example
# Create a matrix of rays
rays = [Ray(SA[x, y, 1000.0], SA[0.0, 0.0, -1.0]) for x in -500:100:500, y in -500:100:500]
results = intersect_ray_shape(rays, shape)
# Process results while preserving grid structure
for i in 1:size(results, 1), j in 1:size(results, 2)
if results[i, j].hit
println("Ray at ($i, $j) hit at $(results[i, j].point)")
end
endLow-Level Functions
AsteroidShapeModels.intersect_ray_triangle — Functionintersect_ray_triangle(ray::Ray, v1::AbstractVector{<:Real}, v2::AbstractVector{<:Real}, v3::AbstractVector{<:Real}) -> RayTriangleIntersectionResultPerform ray-triangle intersection test using the Möller–Trumbore algorithm.
Arguments
ray: Ray with origin and directionv1: Triangle vertex 1v2: Triangle vertex 2v3: Triangle vertex 3
Returns
RayTriangleIntersectionResultobject containing the intersection test result
Algorithm Details
This implementation has the following characteristics:
- No backface culling: Triangles are hit from both sides (front and back)
- Forward rays only: Only intersections in the ray direction are detected (distance > 0)
- No self-intersection: Rays starting exactly on the triangle surface typically miss due to numerical precision
Example
# Ray from above hits triangle on XY plane
ray = Ray(SA[0.5, 0.5, 1.0], SA[0.0, 0.0, -1.0])
v1, v2, v3 = SA[0.0, 0.0, 0.0], SA[1.0, 0.0, 0.0], SA[0.0, 1.0, 0.0]
result = intersect_ray_triangle(ray, v1, v2, v3)
# result.hit == true, result.distance ≈ 1.0
# Ray from below also hits (no backface culling)
ray_below = Ray(SA[0.5, 0.5, -1.0], SA[0.0, 0.0, 1.0])
result = intersect_ray_triangle(ray_below, v1, v2, v3)
# result.hit == true
# Ray pointing away misses (backward intersection rejected)
ray_away = Ray(SA[0.5, 0.5, 1.0], SA[0.0, 0.0, 1.0])
result = intersect_ray_triangle(ray_away, v1, v2, v3)
# result.hit == falseintersect_ray_triangle(ray::Ray, shape::ShapeModel, face_idx::Integer) -> RayTriangleIntersectionResultPerform ray-triangle intersection test for a specific face in a shape model.
Arguments
ray: Ray with origin and directionshape: Shape model containing the triangleface_idx: Index of the face to test (1-based)
Returns
RayTriangleIntersectionResultobject containing the intersection test result
Algorithm Details
This function uses the same Möller-Trumbore algorithm as the base implementation:
- No backface culling (triangles are hit from both sides)
- Forward rays only (distance > 0)
- Inlined for performance
Example
shape = load_shape_obj("asteroid.obj")
ray = Ray(SA[0.0, 0.0, 100.0], SA[0.0, 0.0, -1.0])
result = intersect_ray_triangle(ray, shape, 1) # Test first faceintersect_ray_triangle(ray::Ray, nodes::AbstractVector, faces::AbstractVector, face_idx::Integer) -> RayTriangleIntersectionResultPerform ray-triangle intersection test for a specific face given nodes and faces arrays.
Arguments
ray: Ray with origin and directionnodes: Array of node positions (3D vectors)faces: Array of face definitions (each face is an array of 3 node indices)face_idx: Index of the face to test (1-based)
Returns
RayTriangleIntersectionResultobject containing the intersection test result
Algorithm Details
This function uses the same Möller-Trumbore algorithm as the base implementation:
- No backface culling (triangles are hit from both sides)
- Forward rays only (distance > 0)
- Inlined for performance
Notes
This is a lower-level interface useful when working directly with node and face arrays without a full ShapeModel structure.
AsteroidShapeModels.intersect_ray_sphere — Functionintersect_ray_sphere(
ray_origin::SVector{3, Float64}, ray_direction::SVector{3, Float64},
sphere_center::SVector{3, Float64}, sphere_radius::Float64,
) -> RaySphereIntersectionResultTest if a ray intersects with a sphere.
Arguments
ray_origin: Starting point of the rayray_direction: Direction of the ray (will be normalized internally)sphere_center: Center of the spheresphere_radius: Radius of the sphere
Returns
RaySphereIntersectionResult with fields:
hit::Bool: true if the ray intersects the spheredistance1::Float64: Distance to first intersection point (NaN if no intersection)distance2::Float64: Distance to second intersection point (NaN if no intersection)point1::SVector{3, Float64}: First intersection point coordinatespoint2::SVector{3, Float64}: Second intersection point coordinates
Special Cases
- Ray origin inside sphere: When the ray starts inside the sphere,
distance1 < 0(behind the origin) anddistance2 > 0(in front of the origin). The ray will always hit the sphere from inside. - Ray origin on sphere surface: When the ray starts exactly on the sphere surface,
distance1 ≈ 0. - Tangent ray: When the ray just touches the sphere,
distance1 ≈ distance2. - Sphere behind ray: When both intersection points are behind the ray origin (
distance2 < 0), the function returns no intersection as the sphere is not in the ray's forward direction. - Degenerate cases: Returns no intersection for zero or negative radius spheres, or zero-length ray directions.
Algorithm
Solves the quadratic equation for ray-sphere intersection: |rayorigin + t * raydirection - spherecenter|² = sphereradius²
Example
ray_origin = SVector(0.0, 0.0, 0.0)
ray_direction = SVector(1.0, 0.0, 0.0) # Normalization is handled internally
sphere_center = SVector(5.0, 0.0, 0.0)
sphere_radius = 2.0
result = intersect_ray_sphere(ray_origin, ray_direction, sphere_center, sphere_radius)
if result.hit
println("Ray hits sphere at distances: ", result.distance1, " and ", result.distance2)
println("Entry point : ", result.point1)
println("Exit point : ", result.point2)
endintersect_ray_sphere(ray::Ray, sphere::Sphere) -> RaySphereIntersectionResultTest if a ray intersects with a sphere using Ray and Sphere objects.
This is a convenience overload that extracts the parameters from the Ray and Sphere objects and calls the main implementation.
Arguments
ray: Ray object containing origin and directionsphere: Sphere object containing center and radius
Returns
RaySphereIntersectionResult with intersection details
Example
ray = Ray([0.0, 0.0, 0.0], [1.0, 0.0, 0.0])
sphere = Sphere([5.0, 0.0, 0.0], 2.0)
result = intersect_ray_sphere(ray, sphere)
if result.hit
println("Ray hits sphere at distances: ", result.distance1, " and ", result.distance2)
println("Entry point : ", result.point1)
println("Exit point : ", result.point2)
end