{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} 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 import Data.Array.Accelerate.Array.Sugar ( Elt(..), EltRepr, Tuple(..), fromTuple, toTuple ) import Data.Array.Accelerate.Product import Data.Array.Accelerate.Smart -- standard library import Data.Typeable import qualified Prelude as P -- | An omnidirectional point light source, whose intensity drops off with -- distance from the source. -- data Light = Light Position Colour deriving (P.Eq, P.Show, Typeable) type Lights = Array DIM1 Light lightPos :: Exp Light -> Exp Position lightColor :: Exp Light -> Exp Colour -- | 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 ) -- Get Lights into Accelerate -------------------------------------------------- lightPos l = Exp $ SuccTupIdx ZeroTupIdx `Prj` l lightColor l = Exp $ ZeroTupIdx `Prj` l type instance EltRepr Light = EltRepr (Position, Colour) instance Elt Light where eltType (_ :: Light) = eltType (undefined :: (Position, Colour)) toElt light = let (p,c) = toElt light in Light p c fromElt (Light p c) = fromElt (p,c) instance IsProduct Elt Light where type ProdRepr Light = ProdRepr (Position, Colour) fromProd _ (Light p c) = fromTuple (p,c) toProd _ t = let (p,c) = toTuple t in Light p c prod cst _ = prod cst (undefined :: (Position, Colour)) instance Lift Exp Light where type Plain Light = Light lift (Light p c) = Exp . Tuple $ NilTup `SnocTup` lift p `SnocTup` lift c