Il existe généralement deux méthodes pour résoudre ce problème. De nos jours, ils sont appelés rendu direct et rendu différé. Il y a une variation sur ces deux que je vais discuter ci-dessous.
Rendu vers l'avant
Rendez chaque objet une fois pour chaque lumière qui l'affecte. Cela inclut la lumière ambiante. Vous utilisez un mode de mélange additif ( glBlendFunc(GL_ONE, GL_ONE)
), de sorte que les contributions de chaque lumière sont ajoutées les unes aux autres. Étant donné que la contribution de différentes lumières est additive, le framebuffer obtient finalement la valeur
Vous pouvez obtenir le HDR en effectuant un rendu sur un framebuffer à virgule flottante. Vous effectuez ensuite un dernier passage sur la scène pour réduire les valeurs d'éclairage HDR à une plage visible; ce serait également là que vous implémentez la floraison et d'autres post-effets.
Une amélioration courante des performances de cette technique (si la scène contient beaucoup d'objets) consiste à utiliser un "pré-passage", dans lequel vous effectuez le rendu de tous les objets sans dessiner quoi que ce soit dans le tampon d'image couleur (utilisez glColorMask
pour désactiver les écritures couleur). Cela remplit simplement le tampon de profondeur. De cette façon, si vous rendez un objet derrière un autre, le GPU peut rapidement ignorer ces fragments. Il doit toujours exécuter le vertex shader, mais il peut ignorer les calculs de shader de fragments généralement plus chers.
C'est plus simple à coder et plus facile à visualiser. Et sur certains matériels (principalement les GPU mobiles et embarqués), il peut être plus efficace que l'alternative. Mais sur le matériel haut de gamme, l'alternative gagne généralement pour les scènes avec beaucoup de lumières.
Rendu différé
Le rendu différé est un peu plus compliqué.
L'équation d'éclairage que vous utilisez pour calculer la lumière d'un point sur une surface utilise les paramètres de surface suivants:
- Position de surface
- Normales de surface
- Couleur diffuse en surface
- Couleur spéculaire de surface
- Brillance spéculaire de la surface
- Peut-être d'autres paramètres de surface (selon la complexité de votre équation d'éclairage)
Dans le rendu vers l'avant, ces paramètres accèdent à la fonction d'éclairage du shader de fragment en passant directement du vertex shader, en étant tirés des textures (généralement via les coordonnées de texture passées du vertex shader), ou générés à partir du tissu entier dans le fragment shader en fonction de d'autres paramètres. La couleur diffuse peut être calculée en combinant une couleur par sommet avec une texture, en combinant plusieurs textures, peu importe.
Dans le rendu différé, nous rendons tout cela explicite. Dans la première passe, nous rendons tous les objets. Mais nous ne rendons pas les couleurs . Au lieu de cela, nous rendons les paramètres de surface . Ainsi, chaque pixel de l'écran possède un ensemble de paramètres de surface. Cela se fait via le rendu des textures hors écran. Une texture stockerait la couleur diffuse comme son RVB, et peut-être la brillance spéculaire comme l'alpha. Une autre texture stockerait la couleur spéculaire. Un tiers stockerait la normale. Etc.
La position n'est généralement pas enregistrée. Il est plutôt reconstitué dans la seconde passe par des mathématiques trop complexes pour entrer ici. Il suffit de dire que nous utilisons le tampon de profondeur et la position du fragment d'espace d'écran comme entrée pour déterminer la position d'espace caméra du point sur une surface.
Donc, maintenant que ces textures contiennent essentiellement toutes les informations de surface pour chaque pixel visible de la scène, nous commençons à rendre les quads en plein écran. Chaque lumière obtient un rendu quad en plein écran. Nous échantillonnons à partir des textures des paramètres de surface (et reconstituons la position), puis nous les utilisons simplement pour calculer la contribution de cette lumière. Ceci est ajouté (encore glBlendFunc(GL_ONE, GL_ONE)
) à l'image. Nous continuons à faire cela jusqu'à ce que nous manquions de lumière.
HDR est à nouveau une étape post-processus.
Le plus gros inconvénient du rendu différé est l'anticrénelage. Il faut un peu plus de travail pour l'antialias correctement.
Le plus gros avantage, si votre GPU a beaucoup de bande passante mémoire, est la performance. Nous ne rendons la géométrie réelle qu'une seule fois (ou 1 + 1 par lumière qui a des ombres, si nous faisons du mapping d'ombres). Nous ne passons jamais de temps sur les pixels cachés ou la géométrie qui n'est pas visible après cela. Tout le temps de passage de l'éclairage est consacré à des choses réellement visibles.
Si votre GPU n'a pas beaucoup de bande passante mémoire, le passage de lumière peut vraiment commencer à faire mal. Tirer de 3 à 5 textures par pixel d'écran n'est pas amusant.
Pré-passage léger
Il s'agit en quelque sorte d'une variation du rendu différé qui présente des compromis intéressants.
Tout comme dans le rendu différé, vous restituez vos paramètres de surface à un ensemble de tampons. Cependant, vous disposez de données de surface abrégées; les seules données de surface qui vous intéressent cette fois-ci sont la valeur du tampon de profondeur (pour reconstruire la position), la normale et la brillance spéculaire.
Ensuite, pour chaque lumière, vous calculez uniquement les résultats d'éclairage. Pas de multiplication avec les couleurs de surface, rien. Juste le point (N, L) et le terme spéculaire, complètement sans les couleurs de surface. Les termes spéculaires et diffus doivent être conservés dans des tampons séparés. Les termes spéculaires et diffus pour chaque lumière sont résumés dans les deux tampons.
Ensuite, vous restituez la géométrie, en utilisant les calculs d'éclairage spéculaire et diffus total pour faire la combinaison finale avec la couleur de la surface, produisant ainsi la réflectance globale.
Les avantages ici sont que vous récupérez le multi-échantillonnage (au moins, plus facile qu'avec le différé). Vous effectuez moins de rendu par objet que le rendu direct. Mais l'essentiel sur le report que cela fournit est un moment plus facile d'avoir différentes équations d'éclairage pour différentes surfaces.
Avec le rendu différé, vous dessinez généralement la scène entière avec le même shader par lumière. Ainsi, chaque objet doit utiliser les mêmes paramètres de matériau. Avec le pré-passage de la lumière, vous pouvez donner à chaque objet un shader différent, afin qu'il puisse faire la dernière étape d'éclairage par lui-même.
Cela ne donne pas autant de liberté que le cas de rendu direct. Mais c'est encore plus rapide si vous avez la bande passante de texture à revendre.