710 likes | 865 Views
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
E N D
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
Techniques • 2 méthodes : • Shadow mapping • Basé image • Shadow volumes • Basé objet • Démos
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
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
depth map image plane depth map Z = A lightsource eyeposition eye view image plane,aka the frame buffer fragment’slight Z = B Shadow mapping • A < B : ombre
depth map image plane depth map Z = A lightsource eyeposition eye view image plane,aka the frame buffer fragment’slight Z = B Shadow mapping • A≈B : lumière
Carte de profondeur • Comment la générer ? • Pourquoi c’est compliqué ? • back-buffer • pbuffers • Précision/coût • En xy • En z
Carte graphique Mémoire Processeur graphique Pourquoi c’est compliqué ? Disque dur Mémoire CPU Écran Carte-mère
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’aire projetée d’un élément de surface Le fantôme représente l’ombre de l’objet Source
Cas le pire : source opposée Caméra Source
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
depth map image plane depth map Z = A lightsource eyeposition eye view image plane,aka the frame buffer fragment’slight Z = B Shadow mapping • A < B : ombre
depth map image plane depth map Z = A lightsource eyeposition eye view image plane,aka the frame buffer fragment’slight Z = B Shadow mapping • A ≈ B : lumière
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
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
Compter les plans • Stencil buffer : • 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