Materials and Area Lights

My little raytracer is slowly getting somewhere: I spent a good amount of time to figure out a decent material system. From my past experience I decided to let it do it’s duty in a special “shading” coordinate system where z is the normal at the intersection that is being shaded and x and y are two arbitrary normals chosen to be perpendicular to z. I hope this will be handy once I go for anisotropic materials…

Three spheres, one of them being a light source

Using this as a base it was pretty straightforward to add support for the non-linear approximation of reflectance functions described by Lafortune et al. and hacking in measured data for

  • brushed metal,
  • a blue paint,
  • felt,
  • clay and
  • a primer

Another thing I’ve spent some time on are area lights. The solution I’ve come up includes a type class for everything that is intersectable and everything that has bounds:

class Intersectable a where
   -- | intersects a ray with an object, possibly returning the distance
   --   along the ray where an intersection occured together with the
   --   object properties at the intersection point
   intersect :: Ray -> a -> Maybe (Float, DifferentialGeometry)

   -- | decides if a ray intersects the object
   intersects :: Ray -> a -> Bool

   -- | the default implementation just test if @intersect@ returns something,
   --   should be overridded if this test can be performed cheaper
   intersects r a
      | isJust int = True
      | otherwise = False
      where
            int = intersect r a

class (Intersectable a) => Bound a where
   -- | returns the surface area of the object
   boundArea :: a -> Float

   -- | returns a random point (along with its normal) on the object,
   --   which is preferably visible from the specified point
   boundSample :: a -> Point -> Rand (Point, Normal)

   -- | returns the pdf for the sample chosen by @boundSample@
   boundPdf :: a -> Point -> Float

   -- | the default implementation returns the inverse of the area,
   --   which is suitable for a @boundSample@ implementation which
   --   chooses points uniformly on the surface
   boundPdf a _ = 1.0 / boundArea a

This implicitely expresses an interesting property of the objects the engine deals with: As almost every ray tracer, I quickly came up with a plane (aka half-space). It is easy enough to use a plane for intersection test with a ray. But it would be challenging to use it as a light source–no matter how dim the light would be, an infinite plane would always cause an infinite amount of light to be emitted. And to display an infinite amount of light is somewhere between impossible and boring (all white?).

On the other hand, a sphere easily fits both of the above classes, which makes it a candidate for being plugged into an area light (an an acceleration structure). The struggle I went though while trying to implement an efficient boundSample function for spheres would be worth another post once I’m sufficiently sure I got it right.


About this entry