ombres-temps-reel-version

publicité
Ombres en temps-réel
Nicolas Holzschuch
Cours d’Option Majeure 2
[email protected]
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
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
Shadow mapping
• A < B : ombre
depth map image plane
depth map Z = A
light
source
eye
position
eye view image plane,
aka the frame buffer
fragment’s
light Z = B
Shadow mapping
• A≈B : lumière
depth map image plane
depth map Z = A
light
source
eye
position
eye view image plane,
aka the frame buffer
fragment’s
light Z = B
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
Mémoire
Mémoire
CPU
Processeur
graphique
Carte-mère
Écran
Carte graphique
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
Source
La couleur
représente l’aire
projetée d’un
élément de surface
Le fantôme
représente l’ombre
de l’objet
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
Shadow mapping
• A < B : ombre
depth map image plane
depth map Z = A
light
source
eye
position
eye view image plane,
aka the frame buffer
fragment’s
light Z = B
Shadow mapping
• A ≈ B : lumière
depth map image plane
depth map Z = A
light
source
eye
position
eye view image plane,
aka the frame buffer
fragment’s
light Z = B
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
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
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 r2 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
Résumé : ombres
• Shadow maps :
– Stable, robuste, facile, rapide, aliasage
• Shadow volumes :
– Beau, difficile, complexité algorithmique
• Ombres douces
– Complexe, lent, beau
Téléchargement