In my last post, I talked about using shadow rays to sample a light source directly. Here is our old lovely but noisy path tracer:
This works great provided the light emitting objects are big, but otherwise we get a lot of noise. We also are crossing our fingers we don't get an infinite recursion.
Let's assume we have a magic function direct_light(vec3 p, reflectance_info ref). Can we just do this to the return line above:
return emitted(hitpoint) + direct(hitpont, ref_info) +reflectance*ray_color(r)
NOPE! That would double count direct because that sacttered ray r might hit the light and get the emitted part. So this is better:
return direct(hitpont, ref_info) +reflectance*ray_color(r)
BUT, if you see the light source in the picture, it will be black!
And before we fix that, another problem is that if the surface is a perfect mirror, the direct lighting itself will be too noisy because only one point on the light matters.
So we need something like a "sees the light" flag on a ray.
Man that is pretty ugly! But I don't know a much better way. Be sure to set the viewing ray flags to "should_see_light = true".
Are we done? No. For perfect mirrors, should_see_lights scattered rays should be set true. For diffuse reflectors, false. For glossy objects, it depends on the size of the light source. Here, a lovely technique from Eric Veach is often used. I would go with the "balance heuristic"-- it is easiest. There is a wonderful figure from the paper that shows why this technique is needed:
So are we done? No, there is one more issue. Does the background "emit" light? Isn't it a light source. The answer is you can do it either way. But if it is a light source, and you can hit it, we can get rid of the if (r hits an object) branch-- the background is always hit! But if it has infinite radius where is the hitpoint? I would say these design decisions are not obvious and I am on the fence even after trying all of them.
Next time: what goes in here: direct(hitpoint, ref_info)