Search code examples
game-physicsraytracing

How to combine local lighting with reflections and refractions?


I am working on a raytracer and I am not sure how to combine local lighting(phong) with reflections and refractions? I have TraceRay function in which I compute: localLighting, reflectColor and refractColor.

At this point my final color is simply a sum of those three. But this way I get values over 1 and I don't know how to handle this.

Thanks for any help


Solution

  • This is two separate questions: how to handle reflection/refraction, and how to handle color values greater than 1.

    I'll address the second question first, since it's easier. This is not an error, it's an effect called saturation, or maybe overexposure. It happens in film and digital cameras, and in your eye, as well. The issue is simply that you've got so much light energy reaching your photoreceptor (in the case of an eye, or CCD in a digital camera, or photosensitive compound in a film camera) that the receptor is saturated to it's maximum output value. In other words, you can keep dumping more energy into it, but the receptor won't be able to output any stronger signal, so it will look the same. So the simple solution is to simply saturate all color values at 1.0, which mimics what happens in a real photoreceptor.

    Of course, when this happens in your eye, your pupils constrict so not as much light gets in, and when a photographer has an overexposed shot, they might reduce the aperture, or the exposure time for the same reason. You can do the same in your renderer by simply dividing the color values at every pixel by a fixed amount. This is analogous to constricting the pupil or limiting the exposure time to reduce the amount of light energy that reaches each receptor.

    For reflection and refraction, the only way I know of is through path tracing. The PDF at this link describes the general idea, as well as a related optimization of sorts called metropolis light transport. Essentially what you need to do is recursive ray casting: instead of just casting from the eye through the pixel and into the scene, you need to continue casting rays from the point of intersection (i.e., the point you would normally render at that pixel) randomly out into the scene in order to create a light path composed of multiple line segments, with each segment connecting two points of intersection in the scene, or else connecting a point of intersection with your eye, or with a light source. Then you can trace the path from the light source as it reflects off of each point and figure out how much of the light (of each color) entering the path will be reflected into the next segment at each point. You figure this out using the BSDF of the model, which can approximate using Phong or any other reflection model. The end result tells you how much of the light that entered the path will reach the current pixel.

    For each pixel, you'll need to build many such light paths and let them all contribute to the rendered value of that pixel. This again models a real optical system: light from the scene is bouncing off every point in all different directions and enters your eye from every conceivable path you could possible find through the scene. The idea of path tracing is to sample this set of all possible light paths in order to approximate the contributions from the entire set.

    Now the key is that what you really want is the sum of all the paths in the set, without actually having to trace every single path (there are theoretically an infinite number of them). So there's a statistical trick you can perform: if you divide the light-value of each path you build by the probability of you having built that specific path, the expected value of that quotient is actually the sum of the light-values over the entire set of paths, just like you want. Of course, that's only the expected value, so the chances of getting it with a single path are slim, but if you randomly choose lots of paths and perform the same calculation, then take the mean of this quotient for every path, you should converge towards the desired solution.

    So that's the general idea, the document I linked goes into some details about how to figure out the light value and the probability for each path, as well as some ideas on how to choose which paths to build. Note that what may be confusing is that you're the one who defines the probability of choosing each path, based on the way you probabilistically build the path.