Rendu hors-ligne - Introduction
Le but de ce projet est de réaliser une application permettant de générer des images photo-réalistes en utilisant les extensions du lancer de rayon, qui permettent de prendre en compte des effets de lumière et de matériaux complexes.
Un affichage en 3D comme celui proposé par OpenGL n'est pas nécessaire pour créer un tel programme. L'un des buts de ce projet est néanmoins de vous persuader de l'utilité d'une telle visualisation pour la mise au point des algorithmes, et de vous faire réviser certaines notions acquises dans l'UE Mondes 3D.
1. Structure du projet
Téléchargez et décompressez l’archive sia_td4.zip
Une base de code conséquente est fournie afin que vous n'ayez à vous concentrer que sur les parties graphiques. Tout au long des TD, de nombreuses classes seront à compléter, certaines à créer de toutes pièces, tandis que d'autres ne seront utilisées complètement qu'à la fin du projet.
1.1. Arborescence
Le projet est organisé selon cette arborescence :
- include : contient les fichiers d'entête
- src : continent le code source C++ principal
- ext : contient les dépendances externes
- shaders : contient les shaders GLSL pour l'affichage OpenGL
- data : les fichiers de scène et leurs données associées (maillages, textures, etc.)
- CMakeLists.txt : fichier CMake spécifiant comment compiler le projet
Plusieurs dépendances externes sont utilisées, en voici une présentation succincte :
- Eigen est une bibliothèque C++ d'algèbre linéaire. Elle sera utilisée pour la représentation et manipulation de matrices, vecteurs, transformations géométriques et résolution de systèmes d'équations (documentation).
- pugixml est un analyseur syntaxique XML utilisé pour charger les fichiers de scène.
- tinyexr est un bibliothèque minimaliste pour charger le format de fichier standardisé OpenEXR utilisé pour stocker des images à grande gamme dynamique (HDR).
- lodepng est une bibliothèque pour charger des images au format PNG.
- NanoGUI est une bibliothèque C++ qui permet de créer une fenêtre OpenGL (via GLFW) et des contrôles utilisateurs.
- tinyformat est l'implémentation en C++11 de
printf
etsprintf
avec sûreté de typage. - filesystem est une classe pour manipuler les chemins de façon transparent sous Linux, Windows et Mac OS X.
- lib3ds est une bibliothèque pour charger des maillages au format 3D-Studio ".3DS".
- tinyobjloader est une bibliothèque pour charger des maillages au format Wavefront OBJ.
1.2. Architecture logicielle
Parcourez les fichiers d'entête pour comprendre la structure générale du projet. En voici les classes principales :
Viewer
: le visualiseur/débogueur OpenGL.Scene
: décrit une scène complète.Shape
: classe abstraite dont dérivent les entités géométriques de la scène.Sphere
: dérive deShape
, représente une sphère.Plane
: dérive deShape
, représente un plan infini.Mesh
: dérive deShape
, représente un maillage triangulaire.BVH
: structure d'accélération pour le calcul des intersections rayon-maillage.Material
: la définition complète du matériau d'un objet.Light
: classe abstraite dont dérivent les sources lumineuses.Integrator
: classe abstraite dont dérivent les méthodes d'intégration de l'équation du rendu.Camera
: la caméra de la scène d'où on va créer l'image.Transform
: contient une transformation de l'espace 3D en coordonnée homogènes et son inverse, ainsi que des opérateurs pour transformer correctement vecteurs, positions et normales.Ray
: représente un rayon en 3D (point d'origine et direction).Hit
: regroupe toutes les informations sur l'intersection d'un rayon avec un objet.
Étant donné le nombre important de fichiers sources, je vous recommande vivement d'utiliser un IDE comme, par exemple, QtCreator. Le projet n'utilise certes pas la bibliothèque logicielle Qt, mais QtCreator conserve les avantages de supporter nativement CMake et de proposer une interface graphique intuitive au dessus de GDB.
1.3. Compilation
CMake est utilisé pour gérer la compilation du projet (tutoriel, documentation). Créez un répertoire de build à l'extérieur du dossier contenant les sources, configurez, et compilez :
$ mkdir build
$ cd build
$ cmake ../sia_td1
$ make -j6
Release
. Pour le compiler en mode Debug
(pour pouvoir utiliser GDB), configurez le projet avec la ligne de commande :
$ cmake ../sia_raytracer -DCMAKE_BUILD_TYPE=Debug
À l'issue de la compilation, vous obtiendrez un exécutable sia_raytracer qui assurera deux fonctions :
- la synthèse d'images par lancer de rayon (que vous allez implémenter en grande partie),
- un afficheur de fichier OpenEXR effectuant un tone-mapping minimaliste (conversion dans l'espace de couleurs sRGB et contrôle de l'exposition).
2. Fichiers de scène
Pour faciliter les tests et les comparaisons, les scènes sont décrites par des fichiers au format XML (extension ".scn"). Ce type de fichiers permet de représenter une grande variété de données avec un formalisme rigoureux. Il permet également de facilement extraire et transformer cette information. Le langage de description de la scène peut être vu comme un plan de construction : au fur et à mesure que l'analyseur syntaxique parcoure le fichier XML, il instancie les objets composant la scène.
2.1. Syntaxe
Chaque tag XML représente soit un objet, soit une propriété. Les objets correspondent directement à des instances de classes C++ qui seront allouées sur la pile. Les propriétés sont des paramètres passés au constructeur de l'objet lors de l'instanciation. Par exemple, ce bloc XML permet de créer un matériau diffus rouge :
<material type="diffuse">
<color name="diffuseColor" value="0.8, 0.0, 0.0" />
</material>
Material
, et l'attribut diffuse indique quelle classe fille de Material
doit être utilisé. Le tag <color> va créer une propriété nommée diffuseColor qui sera passée à son constructeur. Si vous ouvrez le fichier "src/material.cpp", vous verrez que le constructeur recherche cette propriété :
Diffuse::Diffuse(const PropertyList &propList)
{
m_diffuseColor = propList.getColor("diffuseColor",Color3f(0.2));
}
REGISTER_CLASS(Diffuse, "diffuse")
Les propriétés suivantes sont actuellement reconnues par l'analyseur syntaxique :
1
2
3
4
5
6
7
<string name="property name" value="arbitrary string"/>
<boolean name="property name" value="true/false"/>
<float name="property name" value="float value"/>
<integer name="property name" value="integer value"/>
<vector name="property name" value="x, y, z"/>
<point name="property name" value="x, y, z"/>
<color name="property name" value="r, g, b"/>
1
2
3
4
5
6
7
<transform name="property name">
<translate value="x, y, z"/>
<scale value="x, y, z"/>
<rotate axis="x, y, z" angle="deg."/>
<rotate value="q1, q2, q3, q4"/>
<lookat origin="x,y,z" target="x,y,z" up="x,y,z"/>
</transform>
Voici un extrait XML définissant une caméra :
1
2
3
4
5
6
7
8
9
<camera type="perspective">
<float name="fieldOfView" value="45.0" />
<integer name="width" value="400"/>
<integer name="height" value="400"/>
<integer name="samples" value="1"/>
<transform name="toWorld">
<lookat target="-11, 5, 0" origin="15, 1, 0" up="0, 1, 0"/>
</transform>
</camera>
Camera
instanciée dans la classe Scene
est distinct de la caméra OpenGL qui affiche la scène dans le Viewer
. Néanmoins, au chargement de la scène, les deux caméras partagent les même attributs et la fenêtre possède la même résolution que l'image finale. À l'exécution, vous pouvez appuyez sur la touche 'c' pour copier la caméra OpenGL vers celle de la scène, ce qui vous permet d'avoir une représentation OpenGL de la scène qui va être rendue. (Attention, la résolution de la caméra correspondra alors à celle de la fenêtre.)
2.2. Utilisation
Lorsque la touche 'l' est pressée, le Viewer
ouvre une fenêtre pour choisir une fichier se terminant par l'extension ".scn" et charge le fichier de scène sélectionné (méthode loadScene
). Vous pouvez également déposer directement le fichier sur la fenêtre pour obtenir le même résultat. Enfin, vous pouvez passer le fichier en ligne de commande à l'exécutable pour le charger au démarrage. Testez par exemple avec le fichier "troisSpheres.scn" :
$ ./sia_raytracer troisSpheres.scn

Ce projet est basé sur ceux de Gilles Debunne, Gaël Guennebaud et de Wenzel Jakob.