Ray Tracing
A ray tracer is software that allows the visualization of a 3D modeled scene based on the theory of inverse geometric optics. The objective is to launch rays from a point of observation (a camera) and follow the inverse optical path of this ray. When there is an intersection between the ray and an object in the environment, a shading calculation determines if the intersected object is illuminated by a light source and evaluates the color that it will reflect and transmit towards the point of observation.
1. The Equation of a Ray
A ray $\vec{r}_{\text {my }}$ is a structure with an origin $\vec{r}_0$ and a direction $\vec{v}$. It allows for the calculation of intersections with geometric shapes in a scene. A time parameter $t$ is used to chronologically order the ray’s intersections with objects in the scene.
A ray parameterized over time has the following form:
where
$ \vec{r}_{\text {ray }}$ : Coordinate hit by the ray after a time $t$.
$\vec{r}_0:$ Origin of the ray.
$\bar{v}$ : Direction of the ray $(\|\bar{v}\|=1$, unit vector $)$.
$t$ : Time elapsed in the ray’s movement.
In computing, the definition of a ray will need the following variables:
| Ray Geometry | Information on the intersected geometry |
|---|---|
Origin position of the ray (\(\bar{r}_0\)) Direction of the ray (\(\bar{v}\)) |
The time \(t\) to intercept the geometry. The normal to the surface \(\bar{n}\) at the interception site. A texture coordinate \(uv\) (if any). Reference to the material applied on the geometry (e.g., to obtain the color of the geometry). |
In this part of the code, we define the components of a ray
template <int RealDim>
class BVHRay
{
public:
using vec_t = eigen_vector_type<RealDim>;
BVHRay(vec_t const& orig, vec_t const& dir,
double dmin = 0, double dmax = std::numeric_limits<double>::max() )
:
M_origin( orig ),
M_dir( dir ),
M_distanceMin( dmin ),
M_distanceMax( dmax )
{}
BVHRay() : BVHRay(vec_t::Zero(),vec_t::Zero()) {}
BVHRay( BVHRay const& ) = default;
BVHRay( BVHRay &&) = default;
BVHRay& operator=( BVHRay && ) = default;
BVHRay& operator=( BVHRay const& ) = default;
vec_t const& origin() const noexcept { return M_origin; }
vec_t const& dir() const noexcept { return M_dir; }
double distanceMin() const { return M_distanceMin; }
double distanceMax() const { return M_distanceMax; }
private:
friend class boost::serialization::access;
template <class Archive>
void serialize( Archive& ar, const unsigned int version )
{
ar & M_origin;
ar & M_dir;
ar & M_distanceMin;
ar & M_distanceMax;
}
private:
vec_t M_origin, M_dir; // ray origin and dir
double M_distanceMin, M_distanceMax;
};
2. Ray-sphere intersection
-
Condition I:point is on ray
-
Condition 2: point is on sphere
-
Let be a sphere of radius r, the ray intersects the sphere so
-
Substitute:
-
this is a quadratic equation in $t$
-
Solution for $t$ by quadratic formula:
-
simpler form holds when $\mathbf{d}$ is a unit vector.
For more explanation, see here.
In this method of template intersect we compute intersection with a ray from the BVH built and return a vector of intersection result
template<typename... Ts>
auto intersect( Ts && ... v )
{
auto args = NA::make_arguments( std::forward<Ts>(v)... );
auto && ray = args.get(_ray);
bool useRobustTraversal = args.get_else(_robust,true);
IntersectContext ctx = args.get_else(_context,IntersectContext::closest);
bool parallel = args.get_else(_parallel,this->worldComm().size() > 1);
bool closestOnly = ctx == IntersectContext::closest;
using napp_ray_type = std::decay_t<decltype(ray)>;
if constexpr( std::is_same_v<BVHRaysDistributed<nRealDim>,napp_ray_type> ) // case rays distributed on process
{
// WARNING: this algo is not good (all_gather of rays then all run bvh), just a quick version for test
auto const& localRays = ray.rays();
std::vector<int> resLocalSize( this->worldComm().size() );
mpi::all_gather( this->worldComm(), (int)localRays.size(), resLocalSize );
std::vector<ray_type> raysGathered;
if ( this->worldComm().isMasterRank() )
{
int gatherRaySize = std::accumulate( resLocalSize.begin(), resLocalSize.end(), 0 );
raysGathered.resize( gatherRaySize );
}
mpi::gatherv( this->worldComm(), localRays, raysGathered.data(), resLocalSize, this->worldComm().masterRank() );
mpi::broadcast( this->worldComm(), raysGathered, this->worldComm().masterRank() );
auto intersectGlobal = this->intersect(_ray=raysGathered,_robust=useRobustTraversal,_context=ctx,_parallel=true);
std::vector<std::vector<rayintersection_result_type>> res;
res.resize( ray.numberOfLocalRay() );
std::size_t startRayIndexInThisProcess = 0;
for ( int p=0;p<this->worldComm().rank();++p )
startRayIndexInThisProcess += resLocalSize[p];
std::copy_n(intersectGlobal.cbegin()+startRayIndexInThisProcess, localRays.size(), res.begin());
return res;
}
else if constexpr ( is_iterable_v<std::decay_t<decltype(ray)>> ) // case rays container are identical all on process (TODO: internal case)
{
std::vector<std::vector<rayintersection_result_type>> resSeq;
resSeq.reserve( ray.size() );
for ( auto const& currentRay : ray )
{
auto currentResSeq = this->intersectSequential( currentRay,useRobustTraversal );
if ( closestOnly && currentResSeq.size() > 1 )
currentResSeq.resize(1);
resSeq.push_back( std::move( currentResSeq ) );
}
if ( !parallel )
return resSeq;
mpi::all_reduce( this->worldComm(), mpi::inplace( resSeq ), [](auto const& x, auto const& y) -> std::vector<std::vector<rayintersection_result_type>> {
std::size_t retSize = x.size();
std::vector<std::vector<rayintersection_result_type>> ret;
DCHECK( x.size() == y.size() ) << "not same size x:" << x.size() << " y:" << y.size();
ret.reserve( retSize );
for ( int k = 0; k < retSize ; ++k )
{
auto const& a = x[k];
auto const& b = y[k];
if ( a.empty() )
ret.push_back( b );
else if ( b.empty() )
ret.push_back( a );
else
{
// WARNING only return one intersection (closest or anyhint)
if ( a.front().distance() < b.front().distance() )
ret.push_back( a );
else
ret.push_back( b );
}
}
return ret;
} );
return resSeq;
}
else // only one ray (all process should have the same ray if parallel=true)
{
auto resSeq = this->intersectSequential( ray,useRobustTraversal );
if ( closestOnly && resSeq.size() > 1 )
resSeq.resize(1);
if ( !parallel )
return resSeq;
}
}

