Problem: Specular reflection and refractive transmission from the light is not seen in standard raytracing.
Caustics are concentrations of light from reflection and transmission. Two examples are shown below, from the work of Henrik Wann Jensen: "Global Illumination using Photon Maps" in Eurographics Rendering Workshop 1996, pp 21-30.
Idea: Cast photons from the light and store them where they hit a surface. When later casting rays from the eye, look up these stored photons and use them in the lighting calculation.
A photon map is a data structure that records where photons from the light land on diffuse surfaces.
Perform three steps:
Each photon is a small data structure that stores
Send photons in random directions from the lights. Use standard raytracing to follow photons through the scene. Upon a photon/surface hit, do only one of
The choice of event, from the three events above, is done probabilistically, with $s, d, t$ being the probability of each event. These probabilities could be $$\begin{array}{l} \displaystyle s = \alpha \; { \max( k_s ) \over \max( k_s ) + \max( k_d ) }\\ \displaystyle d = \alpha \; { \max( k_d ) \over \max( k_s ) + \max( k_d ) }\\ \displaystyle t = 1 - \alpha \\ \end{array}$$
where $k_s, k_d, \alpha$ are the material properties at the ray/surface intersection point and "max" is the maximum of the rgb components.
You should, as always, ensure that $\max(k_s) + \max(k_d) \le 1$ in your material properties.
A photon is stored on a surface if
Once the photon is stored on a surface, raytracing of that photon stops. Raytracing also stops if the photon has made more than some predetermined number of bounces.
Build a kd-tree to hold the photons that have been collected on each surface. A separate 2D kd-tree can used on each surface, or a single 3D kd-tree can be used for the whole scene.
A kd-tree node represents an axis-aligned box. On a surface, for example, this is a 2D box:
The box is split such that half of its photons go into each child. Choose a median photon to define the split position:
Recurse so that each leaf holds no more than some maximum number of photons. Each level of the hierarchy alternates the split axis from $x$, then to $y$, then to $x$ again, then to $y$ again, and so on:
The kd-tree data structure must also provide a function that returns all photons within a distance, $r$, of a position, $x$.
Perform normal raytracing from the eye. At each bounce, query the kd-tree for photons near the bounce point (i.e. the ray/surface intersection point). Consider each of these photons as coming from an additional light source and incorporate them into the Phong model.
Below, $x$ is the bounce point, $C$ is a circle on the surface of radius $r$ around $x$, and each photon (with an arrow for its arrival direction) is shown in red. A query would return the three photons inside the circle.
The rendering equation states that $$L(x,\omega_o) = \int_\Omega f(\omega_i, \omega_o) \; L(x,\omega_i) \; \cos\theta_i \; d\omega_i$$
where:
Discretizing and approximating results in $$\begin{array}{rl} L_\textrm{out}(x,\omega_o) & = \displaystyle \int_\Omega f(\omega_i, \omega_o) \; L(\omega_i) \; \cos\theta_i \; d\omega_i \\ & \displaystyle \approx \sum_{p_i \in C} f(\omega_i,\omega_0) \; L(p_i) \; \cos\theta_i \; \Delta\omega_i \\ & = \displaystyle \sum_{p_i \in C} f(\omega_i,\omega_0) \; { P_i \over \Delta\omega_i \; \cos\theta_i \; \Delta A} \; \cos\theta_i \; \Delta\omega_i \\ \end{array}$$
where:
Then $$L_\textrm{out}(x,\omega_o) \approx {1 \over \pi \: r^2} \sum_{p_i \in C} f(\omega_i,\omega_o) \; P_i$$
To compute this, take each photon in $C$ and multiply its power by $f(\omega_i,\omega_o)$, which is the faction of light coming in from direction $\omega_i$ that leaves in direction $\omega_o$, which you can calculate using the Phong model. Add up all the results and divide by $\pi \: r^2$.
However, since the number of photons in $C$ will be proportional to the number of photons that are in the kd-tree, $\mathbf{L_\textrm{out}}$ should be normalized by dividing it by the total number of photons in the photon map, then multiplying by some constant. This constant should really be chosen according to the power of the light sources, but can be chosen heuristically to get caustics of "reasonable" brightness.
This means that more photons in the area around $x$ result in more outgoing radiance.