packages feed

accelerate-examples-1.0.0.0: examples/ray/Ray/Intersect.hs

{-# LANGUAGE NoImplicitPrelude   #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators       #-}

module Ray.Intersect
  where

-- friends
import Common.Type
import Scene.Object

-- frenemies
import Data.Array.Accelerate                                    as A


-- | Of two intersection tests, take the nearest.
--
nearest :: forall a. Elt a
        => Exp (Bool, Float, a)
        -> Exp (Bool, Float, a)
        -> Exp (Bool, Float, a)
nearest x y
  = let
        (h1, d1, _ :: Exp a) = unlift x
        (h2, d2, _ :: Exp a) = unlift y
    in
    h1 && h2 ? ( d1 < d2 ? (x, y)         -- both objects intersect; take the nearest
               , h1 ?      (x, y) )       -- only one object intersects


-- | Find the nearest point of intersection for a ray. If there is a hit, then
-- return the origin and normal of the new reflected ray, as well as the colour
-- and shine of the surface that was hit.
--
-- This tests all objects for each ray and just takes the minimum. To scale to
-- larger scenes, this should use a spatial decomposition technique like a
-- bounding volume hierarchy to reduce the search space.
--
-- https://developer.nvidia.com/content/thinking-parallel-part-ii-tree-traversal-gpu
--
-- https://developer.nvidia.com/content/thinking-parallel-part-iii-tree-construction-gpu
--
castRay
    :: forall object. Elt object
    => (Exp object -> Exp Position -> Exp Direction -> Exp (Bool, Float))
    -> Exp object                       -- dummy object (because we have no Maybe types)
    -> Acc (Vector object)              -- objects to test
    -> Exp Position                     -- ray origin
    -> Exp Direction                    -- ray direction
    -> Exp (Bool, Float, object)
castRay distanceTo  dummy objects orig dir
  = sfoldl (\s o -> let (_,   dist, _)  = unlift s      :: (Exp Bool, Exp Float, Exp object)
                        (hit, dist')    = unlift $ distanceTo o orig dir
                    in
                    hit && dist' < dist ? (lift (hit, dist', o), s))
           (lift (False, infinity, dummy))
           (constant Z)
           objects


-- | Check where there is some object closer than a given minimum distance. We
--   stop as soon as there is an intersection.
--
checkRay
    :: Elt object
    => (Exp object -> Exp Position -> Exp Direction -> Exp (Bool, Float))
    -> Acc (Vector object)              -- objects to test
    -> Exp Position                     -- ray origin
    -> Exp Direction                    -- ray direction
    -> Exp Float                        -- minimum distance
    -> Exp Bool
checkRay distanceTo objs orig dir dist
  = fst $ while (\s -> let (hit, i) = unlift s in not hit && i < unindex1 (shape objs))
                (\s -> let i        = snd s
                           (hit, dist') = unlift $ distanceTo (objs ! index1 i) orig dir
                       in  hit && dist' < dist ? (lift (True, i), lift (False, i+1)))
                (constant (False, 0))