Haskell Raytracer Project - Part 3

articles ✒ haskell-raytracer-3

Parts of this tutorial

  1. Bitmap output
  2. Vector handling
  3. Scene drawing

This is the third part of a step-by-step guide to constructing a basic ray-tracer (3d renderer) using the Haskell functional programming language.

Rendering a scene

We ought to define a data-type to represent the scene so we can pass it to our rendering function (that we're about to write).

type Scene = [Surface]

We may as well define our background colour now. I'll use white, since it fits with my website's colour scheme.

bgcolour = [255,255,255]

Next, let's make a function to convert a pixel to the ray associated with it.

rayforpixel :: Vector -> Ray
rayforpixel (Vector pixel) = Ray zerovector (normalise (Vector (1.0:pixel)))

Now a function to find the intersections a ray makes with all the objects in a scene.

srintersections :: Scene -> Ray -> [(Surface, Maybe Scalar)]
srintersections scene ray = [ (obj, intersect ray obj) | obj <- scene ]

And another to pick the first one.

srintersect :: Scene -> Ray -> Maybe (Surface, Scalar)
srintersect scene ray = fstintersect Nothing (srintersections scene ray)
    where fstintersect Nothing ((s, Nothing):xs) = fstintersect Nothing xs
          fstintersect Nothing ((s, Just x):xs) = fstintersect (Just (s,x)) xs
          fstintersect m ((s, Nothing):xs) = fstintersect m xs
          fstintersect (Just (ms, m)) ((s, Just x):xs)
                | m > x = fstintersect (Just (s,x)) xs
                | otherwise = fstintersect (Just (ms, m)) xs
          fstintersect m [] = m

To detect the colour of a ray, we take the first object it intersects (if any) then use its colour.

srdetectcolour scene ray = srdetectcolour' scene ray (srintersect scene ray)
 
srdetectcolour' :: Scene -> Ray -> Maybe (Surface, Scalar) -> Colour
srdetectcolour' scene (Ray rx rv) (Just (s,d)) = surfcolour s
srdetectcolour' scene r Nothing      = bgcolour

Finally, let's bring it all together and make an image from the colours of the rays.

raytrace :: Scene -> Integer -> Integer -> Image
raytrace scene width height = [ srdetectcolour scene (rayfor x y) |
                                        y <- [0..height - 1], x <- [0..width - 1]]
    where rayfor :: Integer -> Integer -> Ray
          rayfor x y = rayforpixel (Vector [((fromInteger x) - ((w - 1.0) / 2.0))
                     / (w / 2.0), (((fromInteger y) - (h - 1.0) / 2.0)) / (h / 2.0)])
          w :: Scalar
          w = fromInteger width
          h :: Scalar
          h = fromInteger height

Download the full source code for the raytracer.

comment on this page