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
RayShapeIntersectionResult
objects, one for each input ray
Throws
ArgumentError
if BVH is not built. Callbuild_bvh!(shape)
before using this function.
Notes
- This function provides a convenient interface that matches
ImplicitBVH.traverse_rays
parameters - 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) -> RayShapeIntersectionResult
Perform 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
RayShapeIntersectionResult
object containing the intersection test result
Throws
ArgumentError
if 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)")
end
intersect_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
RayShapeIntersectionResult
objects, one for each input ray
Throws
ArgumentError
if 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
RayShapeIntersectionResult
objects with the same size as input
Throws
ArgumentError
if 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
end
Low-Level Functions
AsteroidShapeModels.intersect_ray_triangle
— Functionintersect_ray_triangle(ray::Ray, v1::AbstractVector{<:Real}, v2::AbstractVector{<:Real}, v3::AbstractVector{<:Real}) -> RayTriangleIntersectionResult
Perform 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
RayTriangleIntersectionResult
object 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 == false
intersect_ray_triangle(ray::Ray, shape::ShapeModel, face_idx::Integer) -> RayTriangleIntersectionResult
Perform 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
RayTriangleIntersectionResult
object 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 face
intersect_ray_triangle(ray::Ray, nodes::AbstractVector, faces::AbstractVector, face_idx::Integer) -> RayTriangleIntersectionResult
Perform 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
RayTriangleIntersectionResult
object 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,
) -> RaySphereIntersectionResult
Test 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)
end
intersect_ray_sphere(ray::Ray, sphere::Sphere) -> RaySphereIntersectionResult
Test 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