module Data.Glome.Shader where import Data.Maybe(mapMaybe) import Data.List(foldl') import Data.Glome.Vec import Data.Glome.Clr import Data.Glome.Solid import Data.Glome.Trace --LIGHTS-- data Light = Light { litpos :: !Vec, litcol :: !Color, litfalloff :: Flt -> Flt, litrad :: !Flt, litshadow :: !Bool } -- | Construct a light given a center location and a color. light :: Vec -> Color -> Light light pos clr = Light pos clr (\x -> 1/(x*x)) infinity True --MATERIALS-- -- | Surface properties at a point on an object's surface. -- Much of this is standard whitted-style illumination. -- Plain diffuse/specular suraces can be defined with -- Surface. -- -- Reflection and Refraction have their own constructors. -- AdditiveLayers is a way of stacking textures such that -- the colors are added together. -- -- Blend takes two textures and returns the result of -- cobining them. -- -- Warp is a little stranger; it takes a ray and re-casts -- into a separate scene (or the same one, if you so choose). data Material t = Surface Color Flt Flt Flt Flt Flt Bool | -- color, alpha, ambient, diffuse, specular, shine, dielectric Reflect Flt | -- amount Refract Flt Flt | -- amount, ior Warp (SolidItem t (Material t)) (SolidItem t (Material t)) [Light] (Ray -> Rayint t (Material t) -> Ray) | -- frame, scene, ctx, xfm AdditiveLayers [Material t] | Blend (Material t) (Material t) Flt -- | Uniform texture t_uniform :: Material t -> Texture t (Material t) t_uniform m = \_ _ -> m --SHADER-- -- | Calculate light intensity and direction at the current ray -- intersection. -- We do this up front so we don't have to re-do the shadow tests when we -- evaluate multiple layered textures. mpreshade :: [Light] -> Ray -> SolidItem t (Material t) -> Rayint t (Material t) -> [(Color, Vec)] mpreshade _ _ _ RayMiss = [] mpreshade lights (Ray o dir) scene (RayHit _ hitpos norm _ _ _ _) = mapMaybe illuminate lights where illuminate (Light lpos color falloff rad do_shadow) = let lvec = vsub lpos hitpos in if vdot lvec norm < 0 then Nothing else let llen = vlen lvec ldir = vscale lvec (1/llen) in if llen > rad || (do_shadow && shadow scene (Ray (vscaleadd hitpos norm delta) ldir) (llen - (2*delta))) then Nothing else Just (cscale color (falloff llen), ldir) mpostshade :: [Light] -> [(Color, Vec)] -> Material t -> Ray -> SolidItem t (Material t) -> Rayint t (Material t) -> Int -> (ColorA, [t]) mpostshade ls lights mat ray@(Ray o dir) s rayint recurs = case rayint of RayMiss -> (ca_transparent, []) RayHit d p n xfmray uvw texs _ -> let eyedir = vinvert dir in case mat of Surface color alpha amb kd ks shine dielectric -> let ambient = cscale color amb direct = foldl' cadd c_black $ map illuminate lights illuminate (lcolor, ldir) = let halfangle = bisect ldir eyedir ldotn = fmax 0 $ vdot ldir n blinn = if ks <= delta then 0 else let b = fmax 0 $ ((vdot halfangle n) ** shine) * ldotn in if isNaN b then 0 else b diffuse = vdot ldir n in cscale lcolor ((blinn*ks) + (diffuse*kd)) (Color r g b) = cadd ambient direct resultcolora = ColorA r g b alpha in (resultcolora, []) Reflect refl -> if (refl > 0) && (recurs > 0) then let outdir = reflect dir n (ColorA r g b a, refltags, _) = (trace ls materialShader s (Ray (vscaleadd p outdir delta) outdir) infinity (recurs-1)) in (ColorA r g b (a*refl), refltags) else (ca_transparent, []) Refract _ _ -> (ca_transparent, []) Warp frame scene' lights' xfm -> let (fcolor, ftags, fint) = (trace ls materialShader frame xfmray infinity (recurs-1)) (wcolor, wtags, wint) = (trace lights' materialShader scene' (xfm ray rayint) (ridepth fint) (recurs-1)) in if ridepth fint < ridepth wint then (fcolor, ftags) else (wcolor, wtags) AdditiveLayers ms -> let (cs, taglists) = unzip $ map (\m -> mpostshade ls lights m ray s rayint recurs) ms in (casum cs, (concat taglists)) Blend ma mb weight -> let (ca, tagsa) = mpostshade ls lights ma ray s rayint recurs (cb, tagsb) = mpostshade ls lights mb ray s rayint recurs in (caweight ca cb weight, tagsa ++ tagsb) mmissshade :: [Light] -> Ray -> SolidItem t (Material t) -> (ColorA, [t]) mmissshade _ _ _ = (ca_transparent, []) materialShader = Shader mpreshade mpostshade mmissshade {- -- no shadows, reflection, or lighting flat_shade :: Rayint t -> Ray -> Scene t -> Int -> Int -> ColorA flat_shade ri (Ray o indir) scn recurs debug = case ri of RayMiss -> bground scn RayHit d p n t -> let (Material clr refl refr ior kd ks shine) = t ri in liftcolor clr -}