RayTracing
Casting rays at a triangle mesh: where a ray first crosses the surface, with the face it crosses and the normal there; whether a particle’s step from one point to the next crosses it, and where; and the same first crossing from a walk of the mesh’s BVH written by hand, for when the ready-made queries do not fit (see Rays and segments and Ray queries).
The source is at Examples/RayTracing/main.cpp. See Building for how to compile it
with CMake, GNU Make, or a direct compiler invocation. It reads the armadillo from the
common-3d-test-models submodule unless given another mesh file:
cd Examples/RayTracing
./RayTracing.ex # or ./RayTracing.ex my_mesh.stl
The code that runs on either side
firstHit() and intersects() are called like signedDistance(): from your own loop on the
host, or from your own functor in a kernel. CastTowards holds the mesh by value and casts one ray
from a point towards a target. Its direction is of unit length, so that t is a distance along the
ray, and it returns the whole HitT, which is trivially copyable:
return EBGeometry::firstHit(m_mesh, Ray{a_origin, d / d.length()}, T(0), EBGeometry::Math::Limits<T>::infinity());
HandWritten finds the same crossing by walking the mesh’s BVH itself, with
PackedBVH::traverse() and a BVH::RayQuery (see PackedBVH::traverse()). The key of a box is
the distance along the ray at which the ray enters it; the state is the nearest crossing so far and its
face; the leaf callback asks each group of triangles where the ray crosses it
(TriangleAoSoA::rayCrossing()); and the bound is the nearest crossing, so boxes beyond it are
skipped. Writing the walk yourself is how to keep something the queries do not report, or to stop on a
condition of your own.
Building the mesh
The mesh is read into a TriMeshSDF in a pool, which must outlive it:
const Mesh mesh = EBGeometry::Parser::readIntoTriMeshSDF<T, K, W>(file, pool);
Using it on the host
A ray from in front of the mesh towards it reports the distance to the crossing, the point, the face
(its index in the file), the face’s outward normal, and whether the ray enters the object there. A
particle’s step is a segment: intersects(mesh, from, to) says whether it crosses the surface,
stopping at the first crossing it finds, and firstHit(mesh, from, to) says where, with t the
distance from from. A crossing at the start of a segment counts, so a particle that stopped on the
wall and moves on starts its next search a little past it, with tMin; it then finds where it
leaves the object:
EBGeometry::firstHit(mesh, wall.m_point, to, stepOff);
The hand-written walk then finds the same crossing and face as firstHit().
The same query on a GPU
The pool is frozen and mirrored to GPU::memoryResource(), the mesh is rebased onto the copy, and
GPU::transform() runs CastTowards once per origin in a kernel, one ray per thread. In a plain
C++ build the same lines run on the host; a GPU build without a visible GPU skips them.