Ombres en temps-réel Nicolas Holzschuch Cours d Option Majeure 2 Nicolas.Holzschuch@imag.fr
Ombres en temps-réel Pourquoi faire? Les ombres Shadow maps Shadow volumes Ombres douces
Les ombres : pourquoi? Réalisme accru Positionnement spatial Information sur les objets Informations sur le système graphique : Comment ça marche, pourquoi, Nombreuses extensions, additions,
Exemples + Vidéo
Ombres dures/ombres douces Vidéos
2 méthodes : Shadow mapping Basé image Shadow volumes Basé objet Démos Techniques
Shadow mapping Source lumineuse ponctuelle Principe : Carte de profondeur de la scène Vue depuis la source lumineuse Pour chaque pixel de l image Calculer position par rapport à la source Calculer profondeur par rapport à la source Comparer à la profondeur stockée Égal : lumière, plus grand : ombre
Shadow volume Source lumineuse ponctuelle Principe : Silhouette des objets vus depuis la source Plans infinis s appuyant sur la source et sur chaque arête Définit «volume d ombre» Pour chaque pixel de l image : Compter le nombre de plans entrants et sortants Positif : ombre, nul : lumière
Et maintenant, les détails
Shadow mapping Source lumineuse ponctuelle Principe : Carte de profondeur de la scène Vue depuis la source lumineuse Pour chaque pixel de l image Calculer position par rapport à la source Calculer profondeur par rapport à la source Comparer à la profondeur stockée Égal : lumière, plus grand : ombre
A < B : ombre Shadow mapping depth map image plane light source depth map Z = A eye position fragment s light Z = B eye view image plane, aka the frame buffer
A B : lumière Shadow mapping depth map image plane light source depth map Z = A eye position fragment s light Z = B eye view image plane, aka the frame buffer
Carte de profondeur Comment la générer? Pourquoi c est compliqué? back-buffer pbuffers Précision/coût En xy En z
Pourquoi c est compliqué? Disque dur Carte graphique Mémoire Mémoire CPU Carte-mère Processeur graphique Écran
Comment faire? Le CPU ne peut pas faire le travail : Trop lent Transfert trop lent C est le processeur graphique qui travaille Comment faire pour dessiner la scène sans l afficher? Deux solutions : back-buffer et pbuffers
Double-buffering L affichage peut être lent L utilisateur voit la scène s afficher morceau par morceau Gênant Idée : double-buffer Deux buffers On affiche le front-buffer On dessine dans le back-buffer Quand on est prêt : glutswapbuffers();
Double-buffering Suppose que la carte soit équipée : Coût mémoire supplémentaire (léger) Automatique de nos jours À demander à la création du contexte OpenGL glutinitdisplaymode(glut_depth GLUT_RGB GLUT_DOUBLE); Ne pas oublier d échanger les buffers
Application aux ombres On a un endroit pour dessiner! On dessine la scène une première fois : Avec la matrice de projection de la lumière Directement dans le back-buffer Ensuite, transfert en mémoire On dessine la scène une deuxième fois : Avec la matrice de projection de la caméra Toujours dans le back-buffer Échange des buffers
Problème Résolution du back-buffer limitée : À la résolution de la fenêtre Problèmes d aliasing Si je veux d avantage de résolution : pbuffers Possibilité de rendu sur la carte, par le processeur, dans une zone mémoire spécifique Résolution plus grande que celle de la fenêtre Mais pas illimitée Pas toujours possible, dépend de la carte
Pour chaque pixel Génération de coordonnées de texture Matrice de projection de la lampe + conversion Résultat : (r,s,t) coordonnées de texture r distance à la source (s,t) coordonnées dans la carte de profondeur Comparaison r / carteprofondeur(s,t) Extension OpenGL : GL_ARB_SHADOW ou GL_SGIX_SHADOW
Extensions OpenGL OpenGL : Spécifications (www.opengl.org) Liste de fonctionnalités (glbegin, glend ) Architecture Review Board (ARB) Extensions : Nouvelles fonctionnalités Décision par l ARB (meetings) Extensions «officielles» : http://oss.sgi.com/projects/ogl-sample/registry/ Spécifications approuvées, publiques Nom et prototypes de fonctions publics Différents niveaux d intégration : GL_ARB_EXTENSION, GL_EXT_EXTENSION, GL_CONSTRUCTEUR_EXTENSION
Extensions OpenGL Comment savoir si une extension est présente? glxinfo http://www.delphi3d.net/hardware/index.php (liste cartes+drivers = extensions) glutextensionsupported("gl_sgix_shadow"); On y reviendra au prochain cours
GL_SGIX_SHADOW gltexparameteri(gl_texture_2d, GL_TEXTURE_COMPARE_SGIX, GL_TRUE); gltexparameteri(gl_texture_2d, GL_TEXTURE_COMPARE_OPERATOR_SGIX, GL_TEXTURE_LEQUAL_R_SGIX); Implémentation très simple
Algorithme Désactiver l affichage des polygones Dessiner la scène Transférer le Z-buffer en mémoire Ré-activer l affichage des polygones Affecter la carte de profondeur comme texture Activer la shadow-map Dessiner la scène Échanger les buffers
Algorithme Désactiver l affichage des polygones : glcolormask(0,0,0,0); gldisable(gl_lighting); Permet de gagner du temps La carte graphique travaille moins Dessiner la scène
Algorithme Récupérer le Z-buffer : glcopyteximage2d(gl_texture_2d,0, GL_DEPTH_COMPONENT16_SGIX, 0,0,width,height,0); Alternative : glreadpixels(0, 0, width, height, GL_DEPTH_COMPONENT, taille, pointeur); glteximage2d(gl_texture_2d, 0, GL_DEPTH_COMPONENT16_SGIX, width, height, 0, GL_DEPTH_COMPONENT, taille, pointeur); Double transfert CPU/carte graphique!
Algorithme Ré-activer l affichage des polygones : glenable(gl_lighting); glcolormask(1,1,1,1); glviewport(0, 0, winwidth, winheight); Activer la shadow map Dessiner la scène Échanger les buffers
Shadow mapping Avantages : Très simple à implémenter Code compact Marche toujours (scène quelconque) Inconvénients : Problèmes d échantillonnage (xy et z) Deux passes de rendu (vitesse divisée par deux) Ne pas regénérer systématiquement la shadow map, seulement si la source se déplace Besoin d extensions OpenGL (disponibles?)
Échantillonnage Principal inconvénient Système discrétisé Double discrétisation : caméra et source lum. Conflit de précision
Précision en xy La plus visible Solution : augmenter la résolution de la carte Limite liée à la carte Pas toujours suffisant : Projection de la texture depuis la source Pixels après projection déformés et agrandis Cas idéal : source proche de la caméra Cas le pire : source opposée à la caméra Animal dans les phares (pas bon pour lui)
Cas idéal : lampe de spéléo Caméra La couleur représente l airel projetée e d und élément de surface Source Le fantôme représente l ombrel de l objetl
Cas le pire : source opposée Caméra Source
Source opposée
Résolution en xy Principale source d erreur Solutions : Augmenter la résolution Déformer la shadow map pour augmenter sa résolution près de l œil Résolution adaptative Pas de solution idéale si la source est face à l œil
A < B : ombre Shadow mapping depth map image plane light source depth map Z = A eye position fragment s light Z = B eye view image plane, aka the frame buffer
A B : lumière Shadow mapping depth map image plane light source depth map Z = A eye position fragment s light Z = B eye view image plane, aka the frame buffer
Problèmes de précision
Problème de précision La carte de profondeur est aussi discrétisée en z Besoin de précision : 16 bits, 24 bits Problèmes avec z voisins : Auto-ombrage des surfaces Solution : Déplacer la carte de profondeur (bias) Trouver la valeur idéale : Trop peu : les surfaces s ombrent elles-mêmes Trop : les ombres disparaissent glpolygonoffset();
Variantes : ID-buffer Pour éviter les problèmes d auto-ombrage Une couleur par objet Objet =? Quelque chose qui ne peut pas s ombrer Convexes Ombrage si ID objet ID dans buffer Pas de problème de précision Mais besoin nombreuses ID : 16 bits Problème si objets proches les uns des autres
Précision La résolution effective dépend de la pyramide de vue de la lampe Large cône de vue : résolution gaspillée Plus la pyramide est proche des objets, plus on est précis Rapprocher la pyramide de vue En xy : faible angle d ouverture En z : front plane et far plane rapprochés
Shadow Mapping : résumé Avantages : Très simple à implémenter, code compact Marche toujours (scène quelconque) Prix indépendant de la complexité de la scène Nombreuses variantes pour améliorer la qualité Inconvénients : Problèmes d échantillonnage (xy et z) Deux passes de rendu Artefacts visibles Sources omni-directionnelles?
Shadow volume Source lumineuse ponctuelle Principe : Silhouette des objets vus depuis la source Plans infinis s appuyant sur la source et sur chaque arête Définit «volume d ombre» Pour chaque pixel de l image : Compter le nombre de plans entrants et sortants Positif : ombre, nul : lumière
Shadow volume
Silhouette des objets Travail sur le modèle Pour chaque arête du modèle : Identifier polygones qu elle relie Produit scalaire normale / vecteur vers la source Si produits scalaires de signe différent : arête de silhouette Besoin structure de données sur le maillage Sur-ensemble de la silhouette des objets
Volume d ombre Plans définis par (arête + source) Définit volume d ombre : En fait, plusieurs volumes imbriqués On est à l ombre si on est à l intérieur d au moins un volume Principe : pour chaque pixel, on compte les plans, de l œil jusqu à la surface affichée Entrée/sortie dans le volume Nombre total de plans croisés
Stencil buffer : Compter les plans Autre fonctionnalité OpenGL Buffer auxiliaire, jamais affiché Opérations possibles : Incrémenter/décrémenter le stencil buffer Conditions sur le stencil buffer, actions sur l écran Multiples utilisations : Ombres, réflexions, fenêtres Rendu conditionnel Début de programmation de la carte
Utilisation du stencil buffer Premier rendu de la scène Initialise le Z-buffer Rendu du volume d ombre Pour chaque plan positif : glstencilop(gl_keep,gl_keep,gl_incr); Pour chaque plan négatif : glstencilop(gl_keep,gl_keep,gl_decr); Deuxième rendu de la scène : glstencilfunc(gl_equal, 0, ~0); Pour la partie éclairée
Algorithme : tracé du volume gldisable(gl_lighting); drawscene(); /* La scène, écl. ambiant */ gldepthmask(0); /* Ne plus écrire ds Z-buffer */ glstencilfunc(gl_always, 0, ~0); glenable(gl_stencil_test); glenable(gl_cull_face); glcullface(gl_back); glstencilop(gl_keep, GL_KEEP, GL_INCR); glcolormask(0,0,0,0); /* pas modifier framebuffer */ draw_shadow_volume(); /* plans positifs */ glcullface(gl_front); glstencilop(gl_keep, GL_KEEP, GL_DECR); draw_shadow_volume(); /* plans négatifs */ glcolormask(1,1,1,1); gldepthmask(1); /* On peut écrire ds Z-buffer */
Algorithme : rendu de la scène glstencilfunc(gl_equal, 0, ~0); glstencilop(gl_keep, GL_KEEP, GL_KEEP); glenable(gl_stencil_test); gldepthfunc(gl_equal); glenable(gl_lighting); drawscene();
Shadow volume Avantages : Ombres précises Positions quelconques lumière/caméra Inconvénients : Calcul de la silhouette (sur CPU, év. long) Besoin de modèles fermés, formés de convexes Deux rendus de la scène, plus rendu du volume fill-rate : tracé de nombreux polygones, qui couvrent l écran. Carte limitée en nb. pixels/seconde
Mauvais cas pour le fill-rate
Shadow volume : améliorations Et si la caméra est dans le volume d ombre? Le compte des plans n est plus bon Système général : Prendre un point hors du volume d ombre Compter les plans entre ce point et la surface Exemple de points hors du volume : l infini Méthode zfail
zfail/zpass
zfail gldepthmask(0); glstencilfunc(gl_always, 0, ~0); glenable(gl_stencil_test); glenable(gl_cull_face); glcullface(gl_front); glstencilop(gl_keep, GL_INCR, GL_KEEP); glcolormask(0,0,0,0); draw_shadow_volume(); glcullface(gl_back); glstencilop(gl_keep, GL_DECR, GL_KEEP); draw_shadow_volume(); glcolormask(1,1,1,1); gldisable(gl_cull_face); glstencilop(gl_keep, GL_KEEP, GL_KEEP); gldepthmask(1);
Limites du volume d ombre Le volume d ombre est défini par des plans Les plans vont de l arête à l infini L infini est difficile à gérer En pratique, on coupe à une certaine distance Que se passe t-il si on voit le volume d ombre à cet endroit? Et si la source est proche de la caméra? Il faut que le volume d ombre soit fermé : Si on coupe, on ajoute des polygones de fermeture
Limites du volume d ombre Applications : limiter le fill-rate Plus le volume est petit, plus le fill-rate est bas Couper les plans : far clipping plane Et fermer le volume : Sur les arêtes, par des plans À l infini, par des plans Ça marche encore Pyramide de vue de la source
Dark cap/light cap
Limitations du volume d ombre
Extensions OpenGL GL_EXT_stencil_two_side Pour faire les deux faces du volume d ombre en une seule passe GL_NV_depth_clamp Pour avoir des plans qui vont vraiment à l infini GL_EXT_depth_bounds_test Pour ne rasteriser que les primitives proches de la source
Shadow volume Avantages : Ombres précises Positions quelconques lumière/caméra Si bien programmé, robuste Inconvénients : Calcul de la silhouette (sur CPU, év. long) Scènes spécifiques : modèles fermés, formés de convexes Deux rendus de la scène, plus rendu du volume fill-rate limité
Ombres douces Algorithmiquement plus compliqué Problème de visibilité point-surface Au lieu de point-point Silhouette? Ombre de la somme somme des ombres Plusieurs algorithmes approximatifs
Ombre/pénombre
Combinaison d ombres
Problèmes de silhouette
Ombres douces Accumulation d ombres : Calculer plusieurs ombres ponctuelles Additionner les résultats, moyenne Accumulation buffer Nombre d échantillons élevés Temps de calcul multiplié par # échantillons
Accumulation 4 échantillons 1024 échantillons
Ombres douces Recherche de voisins : Shadow map normale Pour chaque pixel dans la shadow map Rechercher frontière de l ombre la plus proche Donne position + distance (source, récepteur) Coefficient d atténuation fonction du rapport des distances Lent (recherche sur r 2 pixels) Limiter r : taille de la pénombre limitée
Ombres douces Volume d ombres douces : Shadow volume normal Pour chaque arête de la silhouette : Calculer volume englobant la pénombre Pour chaque pixel dans ce volume Calculer coefficient d atténuation Beau, réaliste Problèmes de fill-rate multipliés par 2
Résumé : OpenGL OpenGL : Z-buffer Double-buffer Pbuffers Extensions Stencil buffer Accumulation buffer Cartes graphiques : Rendu en plusieurs passes Programmables (en un sens) utilisées pour faire des choses complexes (ombres) Ce n est que le début
Shadow maps : Résumé : ombres Stable, robuste, facile, rapide, aliasage Shadow volumes : Beau, difficile, complexité algorithmique Ombres douces Complexe, lent, beau