Rapport synthèse d’image
Lancer de rayon en Ocaml
Master d’informatique, première année

D.Sevat & R.Sunier

Table des matières

Chapitre 1  Introduction

L’objectif de ce projet, est de présenter de manière pédagogique l’implémentation de l’algorithme du lancer de rayon en langage Ocaml.

Nous étudierons le principe de ce moteur de rendu en 3 dimensions ainsi qu’une partie de ses différentes optimisations. De manière à être le plus pédagogique possible, nous présenterons points par point, du plus simple au plus complexe, la création de ce moteur en expliquant la théorie, le code Ocaml, et en présentant diverses captures des résultats. (Pour les parties de code commentées, seule la partie intéressante sera présenté dans le rapport, le tout sera accessible en annexe ainsi que les fonctions mathématiques redéfinies comme le produit scalaire ou encore le produit matriciel)

Pour implémenter le lancer de rayon en Ocaml, il nous a fallut étudier de prime abord son fonctionnement puis le langage demandé. Pour l’étude du fonctionnement du lancer de rayon, merci à Grégory Massal, son site nous a été d’une très grande aide (et aussi d’une très grande source d’inspiration pour remplir ce rapport, la trame générale et certains textes et/ou images ont été directement tirés de son site.)

Concernant les optimisations réalisées, nous avons mis en place le moteur, des primitives de bases, un modèle d’éclairement de base, un modèle d’ombrage, un modèle d’éclairement amélioré, un anti-crénelage (anti-aliasing), un mode de rendu avec gestion des réflexions, et une mise en place d’un limiteur d’exposition.

Chaque optimisation sus-citées, fera l’objet d’un programme á part entière de manière à ce limiter au juste fonctionnement de celles-ci.

Nous conclurons ce rapport par une sorte de bêtisier avec les résultats aberrant obtenus lors du codage.

Chapitre 2  Introduction au lancer de rayon, principe et implémentation :

2.1  Rappel historique :

Le lancer de rayon (ray tracing en anglais) est une technique de rendu en synthèse d’image simulant le parcours inverse de la lumière de la scène vers l’oeil.

Cette technique simple reproduit les phénomènes physiques que sont la réflexion et la réfraction. Une mise en œuvre naïve du lancer de rayon ne peut rendre compte d’autres phénomènes optiques tels que les caustiques (taches lumineuses créées à l’aide d’une lentille convergente par exemple) et la dispersion lumineuse (la radiosité s’attaque à ce problème).

En revanche, contrairement d’autres algorithmes de synthèse d’image, elle permet de définir mathématiquement les objets à représenter et non pas seulement par une multitude de facettes.

2.2  Principe :

Le lancer de rayon consiste, pour chaque pixel de l’image générée, à lancer un rayon depuis le point de vue (la caméra) dans la scène 3D. Le premier point d’impact du rayon sur un objet définit l’objet concerné par le pixel correspondant.

Des rayons sont ensuite lancés depuis le point d’impact en direction de chaque source de lumière pour déterminer sa luminosité (est-il éclairé ou à l’ombre d’autres objets?). Cette luminosité combinée avec la couleur de l’objet ainsi que d’autres informations éventuelles (angles entre la normale à l’objet et les sources de lumières, réflexions, transparence, etc.) déterminent la couleur finale du pixel.

Il est noter que cette technique fonctionne exactement l’inverse de la nature, qui, elle, lance des rayons de lumière depuis les sources lumineuses vers l’œil ou la caméra en passant par les objets, alors que le lancer de rayons procède de la caméra vers les sources de lumières. L’expérience montre en effet que cette manière de procéder est nettement plus performante.

2.3  Schémas :

Que fait le lancer de rayon?


Figure 2.1: [©Laure Heïgéas]

Le lancer de rayon ou raycasting/raytracing est l’une des méthodes de rendu les plus simples qu’on puisse imaginer. Pour chaque point de l’écran, on envoie un rayon (virtuellement) et l’on teste pour chaque objet de la scène s’il se trouve sur le chemin du rayon ou pas. Le point visible est le point d’intersection le plus proche.

A partir de ce point on fait des calculs d’éclairages, avec raffinements ou pas (ombres, réflexion, lambert, phong, bump, etc..) Nous avons donc plusieurs phases pour un lancer de rayon basique.

Une phase de projection de rayon, une de balayage, une de calcul d’intersection et enfin, une d’affichage du pixel considéré sur l’écran.

Chapitre 3  Lancer de rayon basique

Pour implémenter un lancer de rayon basique, il faut définir plusieurs éléments de base.

3.1  Définition d’un rayon

Un rayon est défini par un point de départ et une direction.

type ray = orig: vec; dir: vec où vec est un tableau de flottant représentant les coordonnées x,y,z

3.2  Définition d’un objet de scène (sphère):

Une sphère est définie par son centre, son rayon ainsi que sa couleur. type sphere = center: vec; radius: float ; color: vec ;

3.3  Définition de la source lumineuse

On définit la ou les lumière(s) de la scène par sa position dans l’espace ainsi que sa couleur. type light = l_pos : vec ; l_col : vec

3.4  Définition de la scène

Une scène est composée d’un ou plusieurs objets sous forme d’un arbre ou chaque nœud est un groupe. type scene = Sphere of sphere | Group_sp of sphere * scene

3.5  Implémentation du moteur basic

L’essentiel pour le modèle basic du lancer de rayon étant défini, passons au moteur proprement dit.

Le but du lancer de rayon étant de tester si un rayon intersecte un objet de la scène pour savoir si un point est éclairé ou pas, il faut définir une méthode de calcul des intersections.

3.5.1  L’intersection la plus proche

Le rayon est orienté et paramétré par t, distance arithmétique au point de départ.


Figure 3.1: [©Grégory Massal]

On ne se déplace que dans un seul sens, donc on prend le t de départ derrière l’écran le plus loin possible (valeur arbitraire 20000 ce qui englobera toute la scène en tenant compte du point de départ du rayon).

Le code qui détecte les collisions est réduit au minimum. Pour chaque sphère de la scène on calcule les points d’intersection rayon/sphère qui sont au nombre de deux. On prend le plus proche et l’on compare sa distance au précédent point d’intersection trouvé (ou à la distance de clipping).

La sphère qui a le point d’intersection le plus proche est la sphère courante.


Figure 3.2: [©Grégory Massal]

Le calcul des points d’intersection avec la sphère revient à résoudre des équations polynomiales du second degré.


Figure 3.3: [©Grégory Massal]

Si le delta trouvé et inférieur à 0, il n’y a pas d’intersection entre le rayon et la sphère, si le delta est positif, alors nous avons deux points d’intersection. L’astuce est de déterminer ensuite quelle est la valeur la plus proche de l’observateur, la seconde étant généralement de l’autre coté de l’objet donc invisible.



Voici le code ocaml du calcul d’intersection:

let intersect ray scene = let rec aux ((lambda, _, _, _) as hit) = function | Group_sp (sphere, scenes) -> let ((lambda', _, _ , _) as hit') = aux hit (Sphere sphere) in if lambda' >= lambda then aux hit scenes else aux hit' scenes | Sphere sphere -> let lambda' = ray_sphere ray sphere lambda in if lambda' >= lambda then hit else let normal = ray.orig +|-| lambda' *-| ray.dir in (lambda', normal, sphere.center, sphere.color) in aux (infinity, vec3 0. 0. 0. 0., vec3 0. 0. 0. 0., vec3 0. 0. 0. 0.) scene

Cette fonction prend en paramètre le rayon et ses coordonnées (nous rappelons que c’est un vecteur doté d’un départ et d’une direction) et la scène. La fonction “aux” appelé récursivement sur tous les objets de la scène. Cette fonction renvoie, selon le cas, soit l’infinie (pas d’intersection) ou les coordonnés du point trouvé ainsi que sa normale et d’autres paramètres dont nous parlerons plus loin. (nécessaire au calcul de la couleur)

3.5.2  Calcul de la valeur d’éclairement au point

Une fois le point d’intersection trouvé, il faut connaître la couleur à afficher sur l’écran pour le rayon donné, la couleur de l’objet donné et les lumières de la scène.

L’idée de départ, est d’ajouter à la couleur initiale de l’objet intersecté, l’ensemble des contributions des lumières de la scène.

Une première élimination possible est de se débarrasser des lumières du mauvais coté de la surface. On vérifie cela à l’aide d’un test avec la normale à la surface au point d’intersection. En d’autres termes, si le produit scalaire de la direction de la lumière et de la normale est négatif alors on la néglige. Dans le cas où la fonction de test des intersections renvoie l’infinie, il suffit de donner une couleur de fond à afficher. (noir dans notre programme)



Pour le modèle basique du moteur, nous avons choisi le modèle d’éclairement de Lambert pour commencer car relativement simple à mettre en œuvre. Voici en quoi il consiste:


Figure 3.4: [©Grégory Massal]

Voici le code ocaml du calcul:

let rec ray_trace light ray scene = let lambda, normal, pos, color = intersect ray scene in if lambda = infinity then (0., color) else let col = vec3 0. 0. 0. 0. in let col' = ref 0. in for i=0 to Array.length light - 1 do let n' = normal -|-| pos in let temp' = dot n' n' in let temp = 1. /. sqrt(temp') in let n = temp *-| n' in let dist = light.(i).l_pos -|-| normal in let t = sqrt(dot dist dist) in if (dot n dist) <= 0. then col' := 0. else if t <= 0. then col' := 0. else let light' = (1. /. t) *-| dist in let lambert = (dot light' n) *. 1. in for j=0 to 2 do col.(j) <- (lambert *. color.(j) *. light.(i).l_col.(j)) +. col.(j) done; done; (0., col)

Les fonctions de bases étant définies, il reste un problème à résoudre. En effet, toutes ces fonctions permettent d’obtenir la coloration d’un point pour un rayon, mais comment faire pour le reste de la scène.

Et bien tout ceci est très simple, il suffit pour chaque pixel de l’image de balayer ligne par ligne colonne par colonne en donnant une nouvelle orientation au rayon lancé. Ainsi, pour chaque pixel de l’image, nous obtenons sa coloration et par conséquent le rendu attendu.



Image de rendu du moteur de base:


Figure 3.5: sphère simple avec deux lumières une de face et une de coté.

Le programme de ce rendu est une version simple du moteur en lancer de rayon. Ce programme doit permettre de comprendre l’implémentation du lancer de rayon dans son ensemble avec des spécifications simples (modèle d’éclairement basique, projection orthographique qui ne permet pas la perspective, pas de réflexion, etc..)

Dans la partie suivante, nous allons voir comment améliorer le rendu de ce moteur. Pas à pas, nous allons présenter d’autres spécifications à implémenter de manière à posséder un rendu plus “réaliste”

Chapitre 4  Les améliorations du rendu

Dans cette partie, nous allons monter comment changer le rendu en complexifiant le code. Pour notre rendu, nous avons décidé d’ajouter les optimisations suivantes:

Chaque mise en place de fonctionnalité sera traité de manière indépendante pour cerner plus précisément l’optimisation du code à effectué et mieux comprendre ces notions.

4.1  Les ombres portées

Si un objet de la scène cache une partie de la lumière à un autre objet, notre programme de base affichera la même intensité lumineuse que s’il n’était pas obturé. Or, ceci ne correspond guère à la réalité. Par conséquent, il faut implémenter une méthode qui permet de vérifier la présence d’un objet entre un objet et une source lumineuse.

Ainsi, on testera pour chaque point si une intersection est trouvée entre un rayon ré émit par la surface de l’objet et les sources lumineuses de la scène. Pour tester, nous prendrons un rayon avec pour origine la normale au point d’intersection et comme direction, l’inverse de la direction de la lumière et nous lançons le test d’intersection (celui vu dans le moteur basique) avec comme paramètre le rayon ré-émit. Ceci à la différence que si le résultat est l’infinie, alors il n’y a pas d’objet obturant l’éclairage donc le calcul de la couleur peut se faire normalement (Lambert) sinon, on applique aucun coefficient à la couleur du pixel au point d’impact et ce pour chaque source lumineuse. Voici le code ocaml permettant cette fonctionnalité:

let light' = (1. /. t) *-| dist in let ray' = {orig=normal ; dir=light'} in let (rl, _, _, _) = intersect ray' scene in if rl <> infinity then col' := 0. else let lambert = (dot light' n) *. 1. in for j=0 to 2 do col.(j) <- (lambert *. color.(j) *. light.(i).l_col.(j)) +. col.(j) done;

Résultat du rendu avec ombre portée:


Figure 4.1: Deux sphères avec éclairage sur le coté et de face. Ombre portée sur la sphère blanche.

4.2  La gestion des réflexions

L’algorithme du lancer de rayon est à même de gérer les réflexions des objets entre eux. Dans cette section, nous étudierons le cas de la réflexion parfaite qui n’est qu’une approximation de la réalité. Selon la loi de Descarte, un rayon lumineux possède un angle d’incidence par rapport à la normale de la surface sur laquelle le rayon réfléchit , et repart avec l’opposé de cet angle.


Figure 4.2: La gestion des réflexions

Le rayon issus de l’observateur fonctionne de la même manière qu’un rayon émit par une source lumineuse, par conséquent, il suffit de ré émettre un rayon avec pour origine le point d’intersection et pour direction l’angle obtenu. Il faut réitérer pour chaque objet rencontré par ce rayonde manière à obtenir toutes les contribution des autres objets. Pour gérer la qualité de la réflexion ( objet plus ou moins réfléchissant) on applique un cœfficient (entre 0 et 1, ou 0 indique une réflexion nulle et 1 une réflexion parfaite)sur l’objet qui multiplie la valeur du rayon incident. De manière à ne pas obtenir de réflexion infinie qui bouclerai infiniment le programme, nous limitons le nombre de réflexion à 10 itération.

Voici le code ocaml de la réflexion:

On rajoute un paramètre à l’objet sphère qui correspond au coefficient:

type sphere = { center: vec; radius: float ; color: vec ; s_ref : float } let reflexion ray light scene = let coeff = ref 1. in let col = vec3 0. 0. 0. 0. in let lambda' = ref 0. in for i = 0 to 9 do if !lambda' <> infinity then let (lambda, normal, n, reflection) = ray_trace light ray scene col coeff in if lambda <> infinity then let reflet = 2. *. (dot ray.dir n ) in let newdir = ray.dir -|-| (reflet *-| n) in for j = 0 to 2 do ray.orig.(j) <- normal.(j); ray.dir.(j) <- newdir.(j); done; coeff := !coeff *. reflection else lambda' := lambda done; col

Rendu avec réflexion:


Figure 4.3: Quatre sphères avec des indices de réflexion différents. A noter la sphère verte avec un indice de réflexion de 0 (pas de réflexion).

4.3  Amélioration du modèle d’illumination: Modèle Blinn-Phong

Le modèle de Lambert est une approximation de la coloration des pixel mais ne prend pas en compte le point de vue de l’observateur.

Le modèle développépar Blinn sur les travaux de Phong prend en compte la position de l’observateur par rapport à l’objet et la source lumineuse.


Figure 4.4: [©Grégory Massal]



Ce modèle prend en compte des considérations physiques de l’objet qui sont la brillance de cet objet et l’atténuation de cette même brillance. Cette brillance est utilisée sans le cas d’un objet non réfléchissant mais brillante par rapport à un éclairage donné et une position d’observation donnée.( Par exemple, la simulation d’un matériau plastique).

Blinn a rajouté des considérations physiques au modèle empirique de départ. Cela passe par le calcul d’un vecteur intermédiaire h qui est à mi chemin entre la direction de la lumière et celle de l’observateur. Puis le calcul consiste à calculer le produit scalaire de ce vecteur de Blinn avec la normale à la surface. Un petit calcul suffit à se rendre compte que l’éclairage de Blinn est pareillement maximal lorsque le rayon vue est dans la direction réfléchie de la direction de la source de lumière. C’est donc bien un éclairage spéculaire.

Code ocaml du modèle d’éclairement:

if (dot !n dist) > 0. \&\& t > 0. then let light' = (1. /. t) *-| dist in let ray' = {orig=normal ; dir=light'} in let (rl, _, _, _, _) = intersect ray' scene in if rl = infinity then let lambert = (dot light' !n) *. 1. in let fLightProjection = (dot ray'.dir !n) in let fViewProjection = (dot ray.dir !n) in let blinnDir = ray'.dir -|-| ray.dir in let temp_2 = (dot blinnDir blinnDir) in for j=0 to 2 do if temp_2 = 0. then col.(j) <- (lambert *. color.(j) *. light.(i).l_col.(j)) *. !coeff +. col.(j) else let blinn' = (1. /. sqrt(temp_2)) *. (max (fLightProjection -. fViewProjection) 0.) in let blinn = !coeff *. (puiss blinn' 60) in col.(j) <- (lambert *. color.(j) *. light.(i).l_col.(j)) *. !coeff +. blinn *. 1. *. light.(i).l_col.(j) +. col.(j) done; done;

Rendu avec le modèle d’éclairement de Blinn-Phong:


Figure 4.5: Quatre sphères avec des indices de spéculaire différents. On note l’apparition de tache lumineuse.

4.4  Anti-aliasing: méthode supersampling

Afin d’optimiser le rendu, nous avons décidé de mettre en place un anti-crénelage en post-processing. Ceci permettra d’adoucir les contours et les ruptures de couleur ( comme les ombres portées)

Le suréchantillonnage consiste pour une image à résolution x,y à faire :

Quand on parle de moyenne ce n’est pas forcément strictement parlé une moyenne arithmétique et cela peut couvrir en réalité plus de quatre pixels. Même si pour des raisons de conservations d’énergie il ne faut pas qu’un pixel soit calculé à partir de plus ou moins de l’équivalent de quatre pixels (en ajoutant les poids du filtre) sinon il y a bien évidemment amplification ou réduction du signal.



Le code suivant détermine la couleur de chaque pixel en ajoutant la valeur de quatre sous pixels diminué au quart. La position relative de ces quatre sous pixels est libre. Pour des raisons de simplicité on choisit de les ordonner en grille régulière (comme si on avait vraiment calculé une image quatre fois plus grande). On aurait pu les placer de manière à former une grille tournée à 20 degré afin d’améliorer le lissage géométrique. Ou encore de faire varier de manière aléatoire (ou semi régulière) les sous échantillons par pixel afin de réduire les phénomènes de moiré. Voici le code du la méthode:

let sampleRatio=0.25 (*correspond au taux de diminution de la puissance de chaque pixel.*) for i = 0 to 1 do for j = 0 to 1 do let fragmentx = (float x) +. ((float i) /. 2.) in let fragmenty = (float y) +. ((float j) /. 2.) in let d = vec3 fragmentx fragmenty (-10000.) 0. in let color = reflexion {orig=d ; dir=vec3 0. 0. 1. 0.} light scene in r := (min (color.(0) *. 255.) (255.)) *. sampleRatio +. !r; v := (min (color.(1) *. 255.) (255.)) *. sampleRatio +. !v; b := (min (color.(2) *. 255.) (255.)) *. sampleRatio +. !b; done; done;

Rendu avec supersampling et sans (zoom)


Figure 4.6: Reflet et ombre sur objet avec SS.

4.5  Contrôle de l’exposition

Dans le programme basique, nous appliquons une méthode “naïve” pour la majoration de couleur en appliquant un maximum à 255 si les valeurs de couleur le dépassent. On appelle cet opérateur un opérateur de saturation. Qu’est-ce que la saturation ? Ce terme est utilisé partout en électronique, en musique, en photographie etc.. Cela signifie qu’un signal qui variait sur un large intervalle est ramené à un plus petit intervalle en ramenant toutes les valeurs trop grandes à la valeur max du nouvel intervalle. Ainsi avec cette approche les valeurs 1 et 2 seront toutes les deux perçus comme 1.


Figure 4.7: [©Grégory Massal]

Cela marche bien si toutes les valeurs intéressantes sont inférieures à 1. Mais dans la réalité et dans les images de synthèses ce n’est pas le cas. On veut autant de détails présents dans les hautes intensités que de détails présents dans les basses intensités.



Une solution acceptable viendra de la photographie. En photographie argentique on utilise un matériel ou des composents photosensibles migrent (de photo sensible à inerte) au fur et à mesure qu’ils sont "excités" par les photons. Ils ne migrent pas tous d’un coup et la réaction n’est pas linéaire en nombre de photons reçus. Non, ils migrent comme dans une désintégration nucléaire : par demi-vie. En effet la probabilité qu’un photon touche un composant non migré diminue avec le temps d’exposition car de plus en plus de composants ont migré. Le négatif noircit donc par une fonction exponentielle de l’intensité de la lumière reçue et du temps d’exposition. Comme il s’agit du négatif la fonction d’intensité perçue est donc 1 - exp(lambda * I * durée).


Figure 4.8: [©Grégory Massal]

Il nous suffit par conséquent de définir un coefficient d’exposition pour nos images de manière à éviter une saturation trop importante lors d’un éclairage intensif. (valeur de l’éclairage supérieur à 1)

Code ocaml de cette fonction:

let exposure = -. 1. coefficient d'exposition. r := (1.0 -. exp( color.(0) *. exposure)) *. sampleRatio +. !r; v := (1.0 -. exp( color.(1) *. exposure)) *. sampleRatio +. !v; b := (1.0 -. exp( color.(2) *. exposure)) *. sampleRatio +. !b;



Rendu avec opérateur de saturation et avec opérateur d’exposition:


Figure 4.9: Opérateur de saturation. On perd beaucoup d’information lors d’une exposition à une forte intensité lumineuse.


Figure 4.10: La même scène avec les même lumières mais avec un opérateur d’exposition. Notez la réapparition des informations de reflet sur la boule grise.

4.6  Projection conique

Dans le moteur basique, nous avons utilisé une projection orthographique pour lancer les rayons, mais cette méthode simpliste ne permet pas d’apprécier les distances en profondeur des différents objets. En d’autre termes, cette méthode ne permet pas la réduction d’objet à distance du à la perspective.

Pour simuler cette perspective, nous avons choisi d’implémenter la méthode de projection conique.


Figure 4.11: La même scène avec les même lumières mais avec un opérateur d’exposition. Notez la réapparition des informations de reflets sur la boule grise.

La perspective conique a été inventée par Filippo Brunelleschi en 1415 devant le baptistère de Florence. Cette invention a ouvert la voie à la Renaissance artistique.

Il s’agit d’une projection selon un faisceau de droites passant par un même point (l’œil, ou l’observateur) sur une surface (le tableau).

La perspective cônique est en parfait accord avec l’humanisme (pensée qui apparaît pendant la Renaissance; elle consiste à valoriser l’Homme, à le placer au centre de son univers): l’Homme (ici l’œil de l’observateur) au centre de l’univers (la perspective).

La perspective conique porte son nom du fait que les droites reliant l’œil de l’observateur aux contours d’un objet forment un cône. On parle aussi de projection centrale. (extrait wikipédia)



Pour simuler ceci dans notre moteur, il nous a fallu modifier notre façon d’envoyer les rayons, c’est à dire ne plus les envoyer avec une direction orthogonale mais le envoyer avec une direction en fonction du point de départ et du plan.

Voici le code qui l’implémente:

let projectionDistance = 0.5 *. float(imgx) /. ((tan 0.017453292519943295769236907684886) *. 0.5 *. 90.) for i = 0 to 1 do for j = 0 to 1 do let fragmentx = (float x) +. ((float i) /. 2.) in let fragmenty = (float y) +. ((float j) /. 2.) in let dirx = ((fragmentx -. 0.5 *. float(imgx)) /. projectionDistance) in let diry = ((fragmenty -. 0.5 *. float(imgy)) /. projectionDistance) in let dirz = (1.) in let dir = vec3 dirx diry dirz 0. in let d = vec3 ((1. /. sqrt((dot dir dir))) *. dirx) ((1. /. sqrt((dot dir dir))) *. diry) ((1. /. sqrt((dot dir dir))) *. dirz) 0. in let color = reflexion {orig=vec3 (0.5 *. (float imgx)) (0.5 *. (float imgy)) 0. 0. ; dir=d} light scene in r := (1.0 -. exp( color.(0) *. exposure)) *. sampleRatio +. !r; v := (1.0 -. exp( color.(1) *. exposure)) *. sampleRatio +. !v; b := (1.0 -. exp( color.(2) *. exposure)) *. sampleRatio +. !b; done; done;



Rendu avec gestion de la perspective:


Figure 4.12: Les sphères sont dans le même repère et de même taille mais à une profondeur de la scène différente.

Chapitre 5  Sources & Binaires

Chapitre 6  Conclusion

Pour conclure, nous dressons un bilan de ce qui a été fait et de ce qui pourrait être fait avec du temps supplémentaire, ainsi que les difficultés rencontrées au cours du projet.



Le programme complet de notre rapport permet donc d’obtenir des rendus ou de nombreux phénomènes physiques sont approchés. Que ce soit par la simulation de l’ombre portée, des réflexions propre à certain matériau, de la gestion de la spécularité, des phénomènes d’exposition couramment rencontrés en photographie, et de la perspective propre à la vision humaine.

Il existe encore cependant beaucoup de phénomène à retranscrire comme la réfraction de certain matériau, leur rugosité, leur texture, mais ces optimisations reste à être implémentées.

Nous aurions aimé aussi mettre en place d’autre primitive tel que le cube et le plan mais nous avons préféré nous préoccuper de la qualité du rendu en particulier.

Peut être que ceci fera l’objet d’une optimisation hors projet.

Deux difficultés majeures ont été rencontrées dans ce projet :

Cependant, ce projet fut très motivant et nous a permis d’approfondir la notion du rendu vu en cours. Maintenant, nous allons vous présenter quelques aberration de rendu obtenues lors de nos essais.

Chapitre 7  Annexe

7.1  Le bêtisier du lancer de rayon :


Figure 7.1: Problème sur le calcul du spécular


Figure 7.2: Problème sur le calcul dans le repère conique.


Figure 7.3: Problème sur le calcul avec l’intersection d’un plan.


Figure 7.4: Mauvais calcul des ombres portées (calcul inverse).


Figure 7.5: Erreur avec l’Anti-aliasing.

Références

[©Grégory Massal]
Grégory Massal, Premiers pas, Raytracer en C++, http://www.massal.net/
[©Laure Heïgéas]
Laure He¨igéas, Lancer de rayon, http://heigeas.free.fr/laure/ray_tracing/

Ce document a été traduit de LATEX par HEVEA