# OM3D - TP2: G-Buffer - Year 2
Dans ce TP vous allez implémenter un G-Buffer, pour préparer l’implémentation du deferred shading dans le TP3. On vous demandera aussi de créer une vue de debug pour pouvoir inspecter les différentes informations présentes dans le G-Buffer.
<!--  -->
## Le Plan
Le projet dessine pour le moment tout les objets de la scène en utilisant un éclairage forward dans une texture HDR. Cette texture est ensuite passée à un shader de tone mapping qui dessine dans un second framebuffer. Ce framebuffer est finalement copié à l'écran (avec `blit()`).
Vous devrez modifier cette architecture pour :
1. D'abord rendre la scène dans un G-Buffer
2. Passer l'ensemble des textures du G-Buffer à un shader de debug, dont le but sera d'en extraire les informations et de les dessiner dans un second framebuffer
3. Blit ce dernier framebuffer à l'écran (d'une manière identique au tone mapping dèja présent dans le code)
Ces modifications nécessiteront de créer :
- de nouvelles textures
- un nouveau framebuffer (pour le G-Buffer)
- un nouveau shader, qui servira à rendre les objets de la scène dans le G-Buffer
- un shader de debug
:::warning
Cette nouvelle architecture de rendu n'utilise plus le tone-mapping. Il devra cependant être ré-intégré dans le TP3, ne le supprimez pas complètement !
:::
## Le G-Buffer
Le shader d'éclairage qui vous est fourni utilise un model diffus simple, et n'a besoin que de la position, de l'albedo et de la normale.
Notre G-Buffer doit donc contenir au minimum l'albedo et les normales de la scène (la position sera reconstruite à partir de la depth).
Le G-Buffer se composera donc de 2 textures:
* La première utilisera le format `RGBA8_sRGB` et contiendra l'albedo dans les canaux RGB.
* La seconde utilisera le format `RGBA8_UNORM` et contiendra les normales dans les canaux RGB. (Pour le moment les normales seront stockées comme de simples vecteurs XYZ, sans encodage particulier).
<div style="text-align: center;">
<img src="https://i.imgur.com/mrefzj6.png" width="80%"><p></p></div>
Les canaux alpha sont volontairement laissés vides, vous êtes libres de les utiliser pour ce que vous voulez.
:::warning
Les textures `RGBA8` ne peuvent que stocker des valeurs positives comprises dans l’intervalle [0; 1]. Il est donc nécessaire de convertir les normales (dont les composants se trouvent dans l’intervalle [-1; 1]) avant de les écrire.
:::
## Le multi render-target (MRT)
OpenGL permet aux fragment shaders d'avoir plusieurs valeurs de sortie, et de rendre plusieurs render-targets en une seule passe (en plus de la depth). Le constructeur de la classe `Framebuffer` qui vous a été fourni accepte un `std::array<Texture*>` en paramètre, ce qui permet de spécifier plusieurs render-targets à sa création.
Écrire dans plusieurs render-targets depuis un fragment shader est relativement simple. Il suffit de déclarer une variable `out` pour chaque render-target, et de spécifier un index en utilisant `layout(location = /* index */)`. L'index de location correspond à l'index de la texture dans le tableau passé au constructeur du framebuffer.
L'application dessine déjà la scène dans un framebuffer (bien que celui-ci n'ai qu'une seule render-target). Inspirez vous du code existant pour créer le framebuffer qui accueillera votre G-Buffer.
:::warning
Toutes les textures utilisées comme render-targets par un même framebuffer doivent impérativement avoir la même taille (mais peuvent avoir différents formats).
:::
## Matériaux
Pour le moment tous les matériaux chargés depuis un fichier glTF utilisent un shader faisant un forward simple (contenu dans `lit.frag`).
Ce shader n'est évidement pas compatible avec la génération d'un G-Buffer (en plus d’être inefficace pour calculer l'éclairage). Vous devrez donc le remplacer par un shader adapté.
Pour le moment on supposera que tout les matériaux chargés depuis un glTF sont opaques et rendus en deferred, vous pouvez donc remplacer le shader pour tout les matériaux chargés.
:::danger
Le shader fourni supporte le texturing et le normal-mapping via les defines `TEXTURED` et `NORMAL_MAPPED`. Votre shader de G-Buffer devra lui aussi supporter ces fonctionnalités.
:::
## Vue de debug
Bien que l'on puisse utiliser RenderDoc pour visualiser le contenu du G-Buffer, il peut être pratique de pouvoir le faire dans l'application directement. Vous devrez donc implémenter des vues de debug pour pouvoir inspecter les différentes informations présentes dans le G-Buffer.
<div style="text-align: center;">
<img src="https://i.imgur.com/Y7L6IwB.png" width="32%">
<img src="https://i.imgur.com/sOj4PoT.png" width="32%">
<img src="https://i.imgur.com/F2qDbUJ.png" width="32%">
Exemple de ce que vos modes de debug doivent afficher.
(albedo à gauche, normales au centre, et depth à droite)
</div>
<p></p>
Cette vue de debug utilisera un nouveau shader qui prendra en entrée les textures du G-Buffer, ainsi qu'un entier indiquant quelle partie du G-Buffer doit être affichée.
Elle sera dessinée en utilisant un seul triangle recouvrant l'écran (d'une manière similaire au tone mapping).
Le mode de debug devra être contrôlé par un menu que vous ajouterez dans l'interface ImGui déjà présente.
Exemple d'interface pour votre menu de debug:

:::info
Vous pouvez trouver des exemples de comment utiliser ImGui dans `imgui_demo.cpp`. En plus du code présent dans la fonction `gui` dans le `main.cpp`.
:::
:::warning
Les valeurs présentes dans le depth buffer, bien qu'entre 0 et 1, ne sont pas distribuées de façon linéaire ou uniforme. Pour que votre mode de debug de depth puisse afficher les depths des objets proches et lointain en même temps, il faudra donc transformer la valeur de depth avant de l'afficher. `depth^0.35` est une transformation donnant des résultats décents.
:::
# Vocabulaire
* Une **Texture** est une image sur le GPU.
* Une **Render-target** est une texture dans laquelle on dessine. Une même texture peut donc être appelée "texture" ou "render-target" en fonction du contexte et de l'utilisation.
* Un **Framebuffer** est un ensemble de render-targets dans lequel ont dessine en même temps.
* Un **`blit`** est une copie d'un framebuffer vers un autre.