Up until now I’ve been cheating in creating images (although cheating is really the point of graphics). As we all know there can be no images without light so let’s add some. First off we need a few structures to represent our lights:

data Light = Point {loc :: Vec3f, color :: Color, intensity :: Float} | Spot {loc :: Vec3f, look_at :: Vec3f, color :: Color, angle :: Float, intensity :: Float} deriving (Show) |

A point light is the simplest light, it simply shines light in every direction from a single point. A spot light is a little more complicated: it shines light in a cone (like a spotlight).

So now what can we do with these things? In an ideal world we would do with them exactly what the real world does with them: We would simulate lightrays spraying out of them bouncing around the scene and eventually entering our simulated camera. However a randomly shot lightray has a very small chance of making it to the camera so it’s computationally infeasible to start from the lights and make our way to the cameras. So we do exactly the opposite, we start from the Camera and anytime we hit a solid we try to find the lights that this ray could have started from. And we call what we find “Light castbacks”

Light castbacks are an extra piece of information that we can pass in to our shaders; they look like this:

data Light_Castback = Light_Castback {dir :: Vec3f, color :: Color, intensity :: Float} deriving (Show) |

With these things we have everything we need to implement some basic lighting. Here’s what the shader looks like:

shader_lit :: Shader shader_lit (Ray_Path (Intersection _ _ norm _ False) cbs) = ((clamp 0 255 (floor (220 * foldr (+) 0 (map (\x -> (normed norm) <.> (normed (dir x))) cbs)))) <*> (1,1,1)) <+> (35,35,35) |

What this does is take all the castbacks from the scene and compute the dot product of the direction of the light and the normal of the surface (clamping it to within an acceptable range and boosting it to a minimum (hacky way to make there be some ambient lighting.) The fruits: