> <\body> <\hide-preamble> \; > >>>> : Moteur d'optique géométrique >|>>> sur Mint ou Debian).> Le but de ce projet est de simuler les lois de l'optique géométrique sur une scène , et ainsi de construire un moteur d'illumination de scènes . Le principe est simple : <\itemize-arrow> Les envoient des rayons lumineux, par exemple dans toutes les directions pour une source isotrope, ou dans une seule direction et de façon étendue pour une source de type "soleil". Les rayons sont interceptés par les de la scène. Suivant l'objet, le rayon peut être réfléchi, réfracté, diffusé, filtré, absorbé L'objet ré-émet alors zéro, un ou plusieurs rayons. Des accumulent l'intensité des rayons qu'ils interceptent sur une matrice de pixels. Le processus d'interception - ré-émission est répété jusqu'à ce que le rayon soit totalement absorbé. On peut alors se servir des pixels des écrans comme image, comme un de caméra ( !) si on a placé une lentille devant, ou comme la luminosité qu'aurait un mur à cet endroit (par exemple pour illuminer une scène d'un jeu de plateforme). Plutôt qu'un long discours, mieux vaut regarder les exemples page 2. On peut voir ça comme une méthode de Monte-Carlo. C'est très inefficace pour construire des images (on appelle ça alors le ) car une toute petite fraction des rayons émis arrivent au "" (comme pour une caméra réelle)<\footnote> Pour faire du rendu d'image réaliste performant, les rayons sont lancés depuis l'écran et sont propagés dans le sens inverse (on appelle ça alors le ). C'est beaucoup moins simple à implémenter pour les processus irréversibles (diffusion). . C'est par contre adapté pour faire une illumination physiquement réaliste (on appelle ça alors l'illumination par ). <\compact> Les différents objets implémentés ici sont : <\itemize-dot> Des lignes ou des arcs de cercles, dont les calculs d'interception sont détaillés dans . Des dioptres (lois de Snell-Descartes et coefficients de Fresnel, détaillées dans ), des miroirs, des objets diffusants ( ou plus sophistiqué), des écrans, des filtres Ou bien des objets non localisés, comme du brouillard. Pour implémenter les couleurs, on choisit de rester proche des lois physiques en attribuant un discret à chaque rayon (ici 4 longueurs d'onde). De plus, il faut prendre en compte la polarisation / , qui se comportent différemment pour la réflexion/réfraction<\footnote> Le plan d'incidence des rayons est toujours le même : le plan où vivent les objets . Ainsi, les polarisations et (relatives au plan d'incidence) sont toujours les mêmes, on peut donc séparer la lumière de façon globale en composantes et , et les traiter séparément. . Ainsi, chaque rayon possède . De plus, on oublie tout phénomène de nature ondulatoire. On travaille alors directement en , plutôt qu'en amplitude, et on les additionne simplement. Cela simplifie largement l'implémentation. Les fichiers / implémentent des structures et fonctions utilitaires classiques : un vecteur et un point avec les surcharges d'opérateurs usuelles, les rotations/translations/homothéties, une structure qui implémente un intervalle angulaire dans > modulo > Le fichier contient des fonctions utilitaires pour l'affichage avec SFML, notamment pour convertir les coordonnées \> en coordonnées fenêtre. La structure est implémentée dans /. Un rayon est défini par son point d'origine, sa direction (angle par rapport à l'horizontale), et son spectre de couleur/polarisation en intensité. La structure contient les 8 composantes ainsi que des routines de manipulation des composantes. Pour décrire le contenu de la scène, on utilise un arbre de classes (voir diagramme page 4), pensé pour être facilement extensible, éviter toute duplication de code, et utiliser le polymorphisme au maximum. On distingue les sources, qui ne font qu'émettre des rayons, de tous les autres objets de la scène. Tous les types de sources dérivent de la classe virtuelle , et implémentent la méthode qui renvoie un Rayon\>. On pourra regarder la classe (), qui implémente une source étendue lambertienne en forme de secteur. Tous les autres objets de la scène dérivent de la classe virtuelle de base , qui déclare l'interface commune utilisée lors de la propagation (interception puis ré-émission) des rayons, et pour l'affichage. Un objet est capable de avec la méthode , et, le cas échéant, de avec (où est une structure opaque renvoyée par , conservant les informations de l'interception) : <\big-figure||pdf>|0.65par|||>> Scénario typique (celui de ) d'utilisation des objets. Cela semble compliqué, mais l'interception et la ré-émission sont séparés pour des raisons de performance : les calculs de ré-émission peuvent être lourds (e.g.lois de Fresnel) et ne sont pas nécessaires à chaque interception (par exemple si le rayon a été intercepté plus en amont par un autre objet). renvoie aussi la distance entre le point d'émission du rayon et l'interception, qui sert à déterminer quel est l'objet le plus proche sur la trajectoire du rayon (voir plus loin pour l'utilisation de ces méthodes). D'un côté, on définit alors des classes (virtuelles) qui s'occupent uniquement de la et de l'interception des rayons. décrit une courbe/interface bien définie, et la structure opaque renvoyée par contient le point d'incidence, l'angle d'incidence dans le repère de la scène, l'angle à la normale de la courbe, et le côté sur lequel le rayon est intercepté. L'interception est enfin implémentée pour deux géométries particulières : décrit un segment, et décrit un arc de cercle. On pourra regarder le header et les méthodes () et (). D'un autre côté, on implémente la et la des rayons dans des classes virtuelles indépendantes de la géométrie : pour la réflexion/réfraction sur un dioptre, pour une surface diffusante, pour les miroirs Ces classes implémentent, elles, la méthode . On pourra regarder (), qui implémente les fameuses lois <\equation*> \>=\>,n*sin>|)>=n*sin>|)>,>=*cos \>-n*cos \>|n*cos \>+n*cos \>>|\|>,R>=*cos \>-n*cos \>|n*cos \>+n*cos \>>|\|>> Enfin, comme illustré sur le diagramme, on combine ces classes pour en faire des objets complètements définis : par exemple , qui est un dioptre plan, hérite des classes et . Les écrans (définis dans ) accumulent les rayons incidents et calculent une intensité moyenne pour afficher ou enregistrer le résultat. On utilise surtout , qui est une matrice () de pixels et qui hérite de . La logique de la propagation des rayons est implémentée dans la classe (/), qui stocke les sources et les objets de façon polymorphique (std::shared_ptr\Objet\\>). Pour illuminer / former une image, on appelle la méthode qui lance les rayons à partir des sources. Pour chaque rayon, la méthode est appelée. Cette méthode effectue les tests d'interception et la ré-émission de façon récursive. Schématiquement, on a <\indent> <\wide-tabular> |||||| <\compact> <\verbatim> <\small> void Scene::propagation_recur (rayon) { \ \ \ \ rayons_re_emis = interception_re_emission(rayon); \ \ \ \ for (ray : rayons_re_emis)\ \ \ \ \ \ \ \ \ propagation_recur(ray); } std::vector\Rayon\ Scene::interception_re_emission (ray) { \ \ \ \ essai_intercept(ray)> sur tous les objets de la scène et sélectionne le premier> \ \ \ \ \ \ \ \ \ \ objet_le_plus_proche-\re_emit(ray, intercept_struct); } >>> (cf.figure page 3). Les rayons sont ainsi propagés sur la scène jusqu'à ce qu'ils soient absorbés par un écran ou qu'ils soient d'intensité négligeable (). Une limite est fixée pour éviter les récursion infinies (par exemple avec une réflexion interne totale dans une lame). Enfin, la méthode est appelée pour afficher tous les objets de la scène dans la fenêtre. Tant que rien ne bouge dans la scène, on répète ce processus (une ) autant que possible pour avoir un résultat de moins en moins bruité sur les écrans (merci au théorème central limite). La classe étend et regroupe tout le code indépendant du reste et qu'on trouverait typiquement dans les , mais pas assez générique pour être dans la classe : création des fenêtres , de quelques objets courants, d'une lentille et d'un écran pour former une image, Le code est fourni avec 4 scènes démonstratives utilisant : (qu'on pourra regarder), , , , et qu'on peut faire tourner avec , , , et respectivement. <\compact> Quelques remarques finales : <\itemize-dot> Tous les attributs de classes qui ne sont pas associés à des contraintes/invariants et qui sont susceptibles d'être utiles à l'utilisateur de la classe sont publiques. L'encapsulation, ce n'est pas embêter l'utilisateur avec des getters, c'est protéger l'utilisateur contre des bêtises. L'extensibilité et la clarté a été préférée à la performance quand le choix se posait. Lorsque les constructeurs par copie et d'affectation ne sont pas déclarés, c'est qu'on laisse le compilateur générer celui par défaut, ou, lorsque marqué dans une classe parent, aucun. <\wide-tabular> ||| On reproduit tout à fait de nombreux effets d'optique géométrique que l'on connait bien : lentilles épaisses pas tout à fait stigmatiques et avec des aberrations chromatiques, fibres optiques On obtient une illumination réaliste (toutefois gourmande en ) d'une scène . La formation d'images (par exemple, à droite, un surface bleue diffusante avec reflet de la source ponctuelle l'éclairant) sont correctes mais un peu ennuyeuses. Cela donne envie d'étendre le programme à une scène (ce qui ne serait pas difficile) pour obtenir de vraies images . |<\cell> |png>||120pt||> >>> <\initial> <\collection> <\references> <\collection> > > > > > > > > > > > <\auxiliary> <\collection> <\associate|figure> |1>|> Scénario typique (celui de |language||Scene::interception_re_emission>) d'utilisation des objets. |> <\associate|toc> |1.Objectifs et méthodes |.>>>>|> > |2.Structure du code |.>>>>|> > |2.1.Objets |.>>>>|> > |2.2.Scène |.>>>>|> > |2.3.Divers |.>>>>|> > |3.Conclusion |.>>>>|> >