L’une des fonctionnalités importantes qui manquaient encore à Raydium était le support des ombres dynamiques. Pour résoudre ce problème, il existe deux grandes solutions : les shadow maps et les shadow volumes, que l’on trouve aussi respectivement sous les désignations Texture-based shadows et stencil shadows, dont voilà les brèves explications.
La seconde solution (les stencil shadow volumes, pour ne vexer personne) est très utilisée dans les jeux actuels ou la lumière compte beaucoup (Doom3, FEAR, …) pour son rendu précis et très réaliste pour les intérieurs. L’idée consiste à utiliser un tampon très spécialisé des cartes 3D, le stencil buffer, utilisé à la base pour "limiter" le rendu à certaines zones. Sans entrer dans le détail, la manipulation consiste à générer un "volume d’ombre", c’est à dire une forme plus ou moins complexe qui représente l’ombre générée par un objet de la scène. Cette opération est plutôt compliquée, et demande par exemple de rendre une première fois la scène depuis le point de vue de la lumière. Une fois ce volume généré, on s’en sert pour remplir ("inverser l’état", en fait) le stencil buffer (hop, un autre rendu), qui va servir à savoir quelle zone est "dans l’ombre". Ensuite, il est possible de rendre la scène en deux fois, une première en activant la lumière pour les "pixels" dont le stencil est à zero, et une autre sans la lumière lorsque le stencil est à 1 (voilà une image simple honteusement volée) :
Ca commence à faire une sacrée tripotée de rendus, et il faut multiplier ça par le nombre d’objets "émetteurs d’ombre" et par le nombre de lumières, et répeter le tout pour chaque image… on obtient l’explication de la (relative) simplicité des modèles et des niveaux de Doom 3, par exemple.
Et encore, la version que je donne ici ne supporte pas la caméra dans l’ombre, ni les objets trop proches des lumières, ni les zones qui peuvent êtres soumises à deux ombres en même temps … (la solution à ces problèmes est souvent nommée "Carmack’s reverse", mais elle apporte de nouvelles contraintes et demande encore plus de calculs).
En bref, trop, beaucoup trop pour un moteur light comme Raydium …
L’autre solution pour faire des ombres un peu plus simples passe par les shadows maps. Le principe est beaucoup plus léger. Il faut ici, pour la première étape, faire un rendu de l’objet pour lequel on souhaite une ombre depuis le point de vue de la lumière. Ce rendu est fait sans lumières, sans textures, sans normales, … bref, juste une silhouette "en noir et blanc" de l’objet en question, et on recopie (sic) le résultat dans une texture.
Ensuite, on fait un rendu "normal" du sol, et encore un autre (toujours le sol) avec une projection de la texture d’ombre depuis le point de vue de la lumière. En 3 passes (3 rendus), on arrive à rendre l’ombre d’un objet sur le sol. Les limites sont cependant très fortes, puisque les objets ne peuvent pas émettre d’ombre sur autre chose que le sol (ou alors il faut encore enchainer de nouveaux rendus de ces objets), il n’y a pas de notion de "niveau" de hauteur (la texture est projetée, donc une voiture dans un tunnel va projeter son ombre à la fois sur le sol et le plafond de ce tunnel) … mais cette technique est nettement plus rapide que les shadow volumes, surtout qu’il est possible d’optimiser certaines passes, en particulier avec le multitexturing (pour les deux passes du rendu du sol), même si de nouvelles passes sont demandées pour chaque nouvel objet.
Ce que je souhaitais pour Raydium, c’était un système d’ombres par forcément précis ni superbe, mais une méthode très généraliste (intérieur, extérieur, objets proches, éloignées des sources de lumières, …), et surtout (c’était la priorité) qui demande le moins de calculs possible, même sur des scènes avec beaucoups d’objets (jeux en réseau, avec moteur physique, … il est pas rare de croiser une centaine d’objets sur un même espace) …
Bref, effectuer un rendu du sol par objet (comme le demande les shadow maps) n’était pas une option valide. J’ai donc tenté une option originale et un peu osée : ne générer qu’une seule shadow maps pour toute la scène ! Du coup je n’ai que 3 choses à faire :
1) rendre en même temps tout mes objets "projeteurs d’ombres" et copier le résultat dans une texture d’assez grande taille (256×256 ou 512×512)
2) rendre toute ma scène (objets et sol)
3) projeter (une seule fois donc) ma texture d’ombre sur le sol
… et ça marche ! Certes, le résultat est très peu précis, mais les scènes prennent enfin une nouvelle dimension !
Ca parrait peut être rien pour ceux qui ne connaissent pas trop Raydium, mais en ce qui me concerne, ça fait 5 ans que je joue avec des scènes sans ombres dynamiques, alors ça me change fortement du rendu habituel. Le système est complétement intégré au moteur (les applications qui utilisent Raydium n’ont qu’un "raydium_shadow_enable()" à faire, et toute la scène gère automatiquement les ombres … je redécouvre certaines applis sous ce nouvel angle, et ManiaDrive (cf article précédent) va probablement bénéficier de cette option.
Mon seul problème avec ce système est qu’il devient très très peu précis sur les grandes maps … la solution consiste bien sur tout simplement à augmenter la taille de la texture des ombres, mais puisque le rendu de la texture est effectué par la carte 3D dans le color buffer, il est impossible de dépasser la taille de l’écran ! (donc 512×512 une fois ramené à la puissance de deux inférieure pour du plein écran 1024×768).
Avant de chercher de nouvelles solutions à ce dernier problème, je dois encore optimiser la chose (et j’ai beaucoup de pistes encore) et ajouter le support des ombres pour les animations (cf quelques articles plus bas, là aussi). Pas faché de voir la fin de ces deux semaines de galère intensive sur ce sujet.
Laisser un commentaire
Vous devez vous connecter pour publier un commentaire.