[3D] Shadow Maps, Shadow Volumes, explications …

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.


Publié

dans

par

Étiquettes :

Commentaires

6 réponses à “[3D] Shadow Maps, Shadow Volumes, explications …”

  1. Avatar de LeGreg
    LeGreg

    Euh, pour les shadow maps, tu n’es pas limité à la taille de l’écran..
    4096×4096 sur les cartes NVIDIA.
    De plus tu n’est pas non plus obligé d’avoir des textures carrés (1024×512 c’est aussi une texture puissance de deux)
    et enfin tu n’es pas limité aux textures puissance de deux sur la plupart des hardwares modernes (c’est à dire
    tous ceux qui supportent les shadow maps) donc 1024×768 c’est une dimension de texture valable.

  2. Avatar de Sot_Viet
    Sot_Viet

    Xfennec, tu n’as toujours pas répondu à mon email stylo. ouinouin.

  3. Avatar de Xfennec
    Xfennec

    LeGreg: Si. Raydium n’est pas un moteur qui demande une carte dernier cri avec des PS 3.0. Il tourne très bien sur une GeForce 2 GTS, par exemple. Les extensions ne peuvent être obligatoire (un render to texture ou un pbuffer feraient l’affaire). Ne parlons même pas des "non power of two".
    Autre point, pour une shadow map "unique" (solution implémentée ici), une texture carrée est obligatoire sinon l’étirement est très visible.

    Sot_Viet: Si, j’ai répondu à ton mail (Thu, 27 Oct 2005 12:06:13 +0200, chez gmail).

  4. Avatar de LeGreg
    LeGreg

    Il s’agit alors d’une limitation d’OpenGL (pas d’extensions ? dur dur..).

    Une geforce 2 GTS (de meme qu’une geforce 1) est capable de texturer depuis 2048×2048.
    et toutes les Geforce (et je crois toutes les radeon) supportent les non power of two (sous direct 3d
    et sans mip mapping).
    Bien entendu c’est peut-etre beaucoup pour ton budget mémoire video sur une carte video bas de gamme
    mais rien n’empeche de "scaler" les dimensions comme font la plupart des jeux. C’est probablement
    un ajout gratuit et bienvenu pour les screenshots ;).

    Sinon la technique que tu exposes n’est pas à proprement parler du shadow mapping
    (dans l’acceptation moderne et popularisée par Pixar du terme).
    Mais bon encore une fois j’imagine bien que tu es limité par ton plus petit commun dénominateur hardware..
    (qui fixe tes limites ? tu te les fixes à toi-même ?)

    LeGreg – Graphics, hdr, voxel, ray tracing

  5. Avatar de Xfennec
    Xfennec

    Sans vouloir être agressif, le ton de tes posts est décidément toujours aussi doctoral … A te lire, je me demande si il est normal que je n’ai aucune envie d’imposer du matos récent aux utilisateurs du moteur …

    Les GeForce font du 2048×2048 ? Ce doit être pour cette raison que la ligne "Raydium: OpenGL implementation maximum texture size: 2048×2048" me passe devant les yeux 40 fois par jour. et le rapport avec la choucroute ?

    DirectX 8 faisait déjà du non power of two ? Étrange, une catégorie très large de matos pas si ancien est incapable de tenir des performances correcte dans ce cas.

    J’abuse du terme shadow mapping comme toute la communauté OpenGL ? C’est peut être que je ne suis pas au niveau de Pixar.

    Quand à l’idée de faire des screenshots plus beau que ce que le moteur sait tenir en réalité …

    Peut être est-il intéressant de mettre tes incommensurables compétences au service d’un projet libre, pour voir …

  6. Avatar de LeGreg
    LeGreg

    désolé si tu as mal pris mes commentaires,
    c’était juste ma manière de contribuer sans doute un peu mal prise.
    Je ne faisais juste que répondre à :
    "Mon seul problème avec ce système est qu’il devient très très peu précis sur les grandes maps …"

    Mais je vois que tu as réfléchi à la question et que mes commentaires
    ne s’appliquent pas dans le cadre de tes limitations.

    Voilà.

    LeGreg – Mon site sur la programmation

Laisser un commentaire