TD3 - BRDF de Ward
Implémentez un nouveau matériau Ward héritant de Diffuse (inspirez-vous de la classe Phong). Dans un premier temps, vous utiliserez l'intégrateur Direct pour tester votre code avec la scène
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = \alpha_y = 0.01\) | \(\alpha_x = \alpha_y = 0.1\) | \(\alpha_x = \alpha_y = 0.25\) | \(\alpha_x = \alpha_y = 0.5\) |
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = 0.2 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.5 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.2\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.5\) |
int_mc qui doit retourner l'estimation de l'intégrale selon l'estimateur de Monte-Carlo \(\langle I \rangle\) avec une densité de probabilité uniforme, soit :
$$\langle I \rangle = \frac{1}{N} \sum_{i=1}^N \frac{f(x_i)}{pdf(x_i)} = \frac{1}{N} \sum_{i=1}^N 5x_i^4$$
Ignorez pour l'instant la variable de sortie varest. Pour générer une variable aléatoire entre \(0\) et \(1\), utilisez (double)rand() / (double)RAND_MAX. Pour changer la graine du générateur aléatoire à chaque appel, utilisez la fonction suivante srand(time(NULL)).
Le main appelle int_mc avec un nombre croissant d'échantillons puis dessine avec gnuplot l'erreur d'estimation en fonction de \(N\). Exécutez plusieurs fois le programme et comparez les tracés.
Calculez maintenant la variance analytique en fonction de N : $$\sigma^2_{est} = \frac{1}{N} \int_0^1 \left(\frac{f(x)}{pdf(x)} - I \right)^2 pdf(x) dx = \frac{1}{N} \int_0^1 (5x^4 - 1)^2 dx$$ Tracez sur le graphique précédent cette variance.
Estimez enfin la variance numérique (variable de sortie varest) à l'aide de l'estimateur :
$$ \langle \sigma^2_{est} \rangle = \frac{1}{N-1} \left\{ \frac{1}{N} \sum_{i=1}^N \left(\frac{f(x_i)}{pdf(x_i)}\right)^2 - \left( \frac{1}{N} \sum_{i=1}^N \frac{f(x_i)}{pdf(x_i)}\right)^2 \right\} $$
Affichez là sur le même graphique.
DirectMats s'inspirant de Whitted mais où la réflexion miroir
sera remplacée par l'échantillonnage uniforme de la BRDF (ignorez la réfraction).
Chargez les deux paramètres suivants dans le constructeur de la classe DirectMats :
<integrator type="direct_mats">
<integer name="samples" value="100"/> <!-- nombre d'échantillions -->
<boolean name="IS" value="false"/> <!-- utilisation de l'échantillonage préférentiel -->
</integrator>
Afin d'obtenir un rendu plus réaliste nous utiliserons une carte d’environnement.
Lorsqu'un rayon (primaire comme réfléchi) n'intersecte aucun objet, au lieu de lui donner la couleur de fond de la scène,
on va chercher sa couleur dans une image représentant l'environnement.
Téléchargez l'archive lightprobe.zip et intégrez la classe LightProbe à votre code.
Cette classe permet de charger des light probes OpenEXR au format panoramic latitude-longitude,
comme celles fournis par l'USC.
Chargez la carte d'environnement LightProbe::load.
Pour obtenir la radiance dans la direction d'un rayon, il vous suffit ensuite d'appeler la fonction LightProbe::intensity.
Testez avec le fichier de scène
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = \alpha_y = 0.01\) | \(\alpha_x = \alpha_y = 0.1\) | \(\alpha_x = 0.5 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.5\) |
Les équations 6 à 8 du même document vous permettront de générer les directions d'échantillonnage.
Ajouter une méthode d'importance sampling à la classe Material qui retournera une nouvelle direction d'échantillonnage à chaque appel.
Dans RenderingWidget::drawContents, lorsque un rayon lancé par un clic utilisateur intersecte un objet,
tirez et affichez avec Line::draw une centaine de directions d'échantillonnage.
Assurez-vous que la distribution de ces directions varie en accord avec les paramètres de la BRDF (taille et anisotropie du lobe).
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = \alpha_y = 0.01\) | \(\alpha_x = \alpha_y = 0.1\) | \(\alpha_x = 0.5 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.5\) |
N'oubliez pas que vous devez également diviser la radiance obtenue dans la direction d'échantillonnage par la probabilité associée (\(pdf\) décrite par l'équation 9).
Pour une plus grande stabilité numérique (et pour réduire le coût de calcul), il est conseillé de pré-diviser la BRDF par cette probabilité.
Dans le cas de Ward, implémentez l'équation 10 dans une méthode Ward::premultBrdf.
Notez que le \(\cos(\theta)\) est désormais inclus dans le calcul de la BRDF ; ne le re-multipliez pas dans l'intégrateur.
Conservez bien la fonction Ward::brdf qui sera appelée lors du calcul de l'éclairage directe.
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = \alpha_y = 0.01\) | \(\alpha_x = \alpha_y = 0.1\) | \(\alpha_x = 0.5 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.5\) |
Nous n'avons considéré dans les exemples précédents que des matériaux de Ward purement métalliques. La BRDF de Ward comporte néanmoins une composante diffuse qu'il vous faut également intégrer. Vous pouvez soit utiliser l'échantillonnage uniforme sur l'hémisphère \((pdf=1/2\pi)\), soit intégrer le \(\cos(\theta)\) dans la distribution d'échantillonnage (cosine weighted hemisphere sampling) qui générera une proportion moins importante de directions à la base de l'hémisphère (dont la contribution est plus faible en raison du \(\cos(\theta)\)).
Modifiez votre fonction d'importance sampling de la classe Ward pour tirer aléatoirement une direction spéculaire ou diffus.
La probabilité de choisir la première par rapport à la seconde devra être proportionnelle au rapport des réflectivités moyennes :
Avg(m_specularColor) / (Avg(m_specularColor) + Avg(m_diffuseColor)).
Notez à quel point la contribution diffuse ré-introduit du bruit dans l'image.
![]() |
![]() |
![]() |
![]() |
| \(\alpha_x = \alpha_y = 0.01\) | \(\alpha_x = \alpha_y = 0.1\) | \(\alpha_x = 0.5 ~~~ \alpha_y = 0.1\) | \(\alpha_x = 0.1 ~~~ \alpha_y = 0.5\) |
Remerciements