GlomeTrace-0.3: Data/Glome/Shader.hs
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
-}