TD7 - Photon Mapping

L'objectif de ce TD est d'implémenter une version simple de l'algorithme de photon mapping qui permettra la visualisation directe (sans final gather) de la photon map pour les surfaces diffuses et utilisera le path tracer pour les autres matériaux. Afin de commencer dans de bonnes conditions, téléchargez l'archive sia_td7.zip.

La structure de données représentant une photon map vous est fournie dans les fichiers kdtree.h et photon.h/.cpp. Vous disposez également du squelette d'un nouvel intégrateur : PhotonMapper. Durant la première passe de l'algorithme, dans la méthode PhotonMapper::preprocess, vous devrez remplir cette photon map en générant des photons depuis toutes les sources lumineuses et en les traçant dans la scène. Durant la seconde passe, dans la méthode PhotonMapper::Li, vous tracerez des chemins depuis la caméra, comme pour le path tracer, sauf que vous utiliserez la photon map à chaque intersection avec une surface diffuse. Ces deux étapes sont détaillées dans la suite.

1. Émission et tracé des photons

Seules les sources étendues émettront des photons. Ajoutez aux classes Scene et AreaLight une méthode samplePhoton. La première sélectionnera aléatoirement une source étendue et appellera la seconde. Celle-ci tirera uniformément une position aléatoire sur la source, puis une direction aléatoire $\omega_0$ sur l'hémisphère orientée selon la normale à la surface (avec Warp::squareToCosineHemisphere). La puissance du photon est $\Phi = \cos(\omega_0)~L_e / pdf = \pi ~ A ~ L_e $ avec $L_e$ la radiance émise par la source et $A$ l'aire de la lampe (donc du rectangle) car, pour un échantillonnage pondéré par le cosinus, $pdf = \cos(\omega_0) / (\pi ~ A)$.

Chaque photon généré est alors tracé dans la scène, comme une chemin lumineux, c'est-à-dire en échantillonnant récursivement une direction de la BRDF (avec importance sampling), en traçant un rayon dans cette direction, et en utilisant le mécanisme de roulette Russe comme critère d'arrêt (utilisez le ratio entre la puissance du photon avant et après le rebond). La pondération associé au matériau (BRDF et PDF) et à la roulette Russe doit être progressivement multipliée à la puissance du photon. À chaque fois qu'une surface diffuse est intersectée, un photon est enregistré dans la photon map. Une fois le nombre visé de photons $N$ atteint (m_photonCount), le KdTree est construit en appelant sa méthode build.

2. Estimation de la radiance

La seconde passe est presque identique à votre implémentation de PathMats::Li. La seule différence est que, lorsqu'un rayon intersectera une surface diffuse, l'algorithme devra chercher directement dans la photon map les photons à proximité du point d'intersection (dans le rayon m_photonRadius) pour calculer une estimation de la radiance, et stopper la récursion.

Vous utiliserez le noyau de reconstruction de Simpson : $$K(x) = \begin{cases} \frac{3}{\pi} \left(1-||x||^2 \right)^2 & \text{si } ||x|| \in [0,1] \\ 0 & \text{sinon} \end{cases} $$ avec $w$ le rayon de recherche / bande passante du noyau de reconstruction.
La radiance sortante peut alors être estimée par : $$L_{o}(p \leftarrow \omega_o) \approx \frac{1}{N~w^2} \sum_{i=0}^{N} K\left(\frac{p-p_i}{w}\right) ~ \Phi_i ~ \rho(p,\omega_o,\omega_i)$$ avec $N$ le nombre total de photons émis depuis la lampe (m_nPaths) même si la somme peut être restreinte aux photons dans la bande passante du noyau de reconstruction.

Pour la Cornell Box, vous devriez obtenir :

photon mapping
1 millions de photons - 1 chemin par pixel
photon mapping
10 millions de photons - 5x5 chemins par pixel