In the last post I talked about the basic principles that allow you to put a raytracer together and I showed the first image that most people get to when building their own. i.e. “THE SPHERE”. This time round lets move on to lighting.
Before getting into the details of how lighting is simulated in a simple raytracer I want to ask you a question, and let you in on a little secret. The question is… where does light come from in the real world? Does it come from the sun? From lightbulbs? Yes and yes, and horrifically, it comes from everybloodywhere else too. Literally everywhere.
Look at a black object in the room. Notice how you can still see a little bit of a highlight somewhere on the object (it’s a shiny surface, it’s dark grey not black, etc.). The light that reached your eye from that object, also reached your face. And it made your face just slightly brighter. Yes, that black object is also a source of light.
Everything is a source of light, and that’s what makes realistic image generation so very very difficult.
So now you know the awful secret, lets get back to simple lighting models like the one I used in the following image…
There are a couple of very basic lighting types which people commonly use when doing simple lighting models. The directional light, and the point light. A directional light does exactly what it says on the tin. Light is simulated as all coming from one direction (a bit like sunlight), and when it hits a surface we calculate the lighting contribution by working out how much the surface points towards the light. i.e. if it points away from the object, it’s not lit at all, if it points directly at the light it’s fully lit.
Mathematically this is done by using a vector for the normal at the surface of the object (n) and calculating the cosine of the angle between this and the light direction (l). This is handily extremely simple to calculate by multiplying the x, y and z components of each vector and summing them. This is called the dot product. And the lighting can be summarised as
lighting = CLAMP(normal . lightdirection)
Note that the CLAMP ensures that we don’t get any negative values coming out of the equations.
Next we move on to the point light source. In this case we simulate an object within the scene which emits light. The lightdirection then depends on the point of the object we’re lighting and it’s direction from the lightsource (i.e. lightdirection = collisionpoint – lightposition).
lighting = CLAMP(normal . NORMALISE(collisionpoint – lightposition))
Note the NORMALISE function which keeps a vector at a length of 1.
We also then add something called attenuation. Imagine you have a ball and you place it right next to a lightbulb. It’ll be extremely bright. Now take the ball to the far side of the room. It’ll be a lot darker. Attenuation simulates this drop in colour over distance. Typically using an r squared formula.
lighting = CLAMP(normal . NORMALISE(collisionpoint – lightposition)) * attenuation
attenuation = lightstrength / SQUARE(MAGNITUDE(collisionpoint – lightposition))
This now gives us what’s known as the diffuse component of light in the scene. Again, if you want a lot more information about the derivation of these formula try google!
Another type of simple lighting often rendered in a scene is the specular light component. This is an attempt to simulate the highlights that you see on shiny objects when they’re lit by a lightsource. Take a look at the following image.
Note how it looks shiny. That’s because it has a narrow specular highlight. Specular highlights can also be wider. This simulates something like a sheen surface. Not perfectly reflective, but not entirely matt either…
So that concludes the simple lighting models you’d use initially in a raytracer. But as mentioned at the top, these lighting models are based on massive simplifications. Still, you can get some great results…
In case you’re wondering. This image uses white balls and a white circle underneath with 3 light sources some distance above. The light sources are coloured red, green and blue, which gives some interesting effects in the shadows. Note that I also added some depth of field to this image, and some reflections, but we’ll get onto those later…