packages feed

accelerate-examples-1.3.0.0: examples/ray/Scene/Light.hs

{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE PatternSynonyms   #-}

module Scene.Light
  where

-- friends
import Common.Type
import Ray.Intersect
import Scene.Object

-- frenemies
import Data.Array.Accelerate                                    as A
import Data.Array.Accelerate.Data.Colour.Names
import Data.Array.Accelerate.Data.Colour.RGB
import Data.Array.Accelerate.Linear.Metric
import Data.Array.Accelerate.Linear.Vector


-- | An omnidirectional point light source, whose intensity drops off with
--   distance from the source.
--
data Light = Light_ Position Colour
  deriving (Show, Generic, Elt)

type Lights = Array DIM1 Light

pattern Light :: Exp Position -> Exp Colour -> Exp Light
pattern Light { lightPos, lightColor } = Pattern (lightPos, lightColor)
{-# COMPLETE Light #-}


-- | Compute the direct lighting contribution of all lights acting on a point on
-- a surface at a given normal direction.
--
--    normal
--         ^           x light
--         |        .
--         |     . objects?
--         |  .
--   ______x______ surface
--       point
--
applyLights
    :: Acc Objects
    -> Acc Lights
    -> Exp Position
    -> Exp Direction
    -> Exp Colour
applyLights objects lights point normal
  = sfoldl (\c l -> c + applyLight objects point normal l) (constant black) (constant Z) lights


-- | Compute the direct lighting at a particular point for a single light
--
applyLight
    :: Acc Objects              -- possible occluding objects, used for shadows
    -> Exp Position             -- point which is being lit
    -> Exp Direction            -- surface normal at this point
    -> Exp Light                -- does this light contribute colour to this point?
    -> Exp Colour
applyLight objects point normal light
  = let
        (spheres, planes)       = unlift objects

        -- What is the direction and distance from the light source to the point
        -- on the surface?
        --
        lp_p                    = lightPos light - point
        dist                    = norm lp_p
        dir                     = (1.0 / dist) *^ lp_p

        -- Calculate the magnitude of the reflected light, if there are no
        -- occluding objects between the light and the surface point.
        --
        mag                     = (normal `dot` dir) / (dist * dist)
        RGB r g b               = unlift (lightColor light)
        refl                    = lift $ RGB (r * mag) (g * mag) (b * mag)
    in
    checkRay distanceToSphere spheres point dir dist || checkRay distanceToPlane planes point dir dist
      ? ( constant black, refl )