Eclairage, projection d'ombres
L'éclairage
Cela se fait à partir des normales en chaque point d'un objet 3D. En gros plus la normale est en direction de la source de lumière, plus le point en question est éclairé (Juste un produit scalaire donc).
Les cartes 3D s'occupent très bien de ce point.
Dans le cas de l'ombrage de Goureau, on calcule l'éclairage aux trois points d'un triangle, puis on interpole entre ces valeurs pour tous les points du triangle.
Dans le cas de l'ombrage de Phong, on interpole le vecteur normale dans le triangle et on calcule l'éclairage en chaque point. Ce calcul d'éclairage peut être précalculé dans une map de Phong.
EDIT: voir la documentation sur l'éclairage par pixel et le bump mapping
La projection d'ombre
Trois méthodes existent actuellement. Mais aucune n'est pleinement satisfaisante actuellement. C'est un domaine en plein essor actuellement.
Shadow Buffer (Shadow map)
On place une caméra à l'endroit où est la source de lumière, et on remplit un Z-Buffer.
Puis depuis la caméra principale, pour tous les points de l'écran on calcule leur position dans le Z-Buffer précédent et on recalcule leur coordonnée Z depuis la caméra. Si le Z provenant du buffer est le même que celui recalculé, le point est éclairé; sinon il est ombré.
Id-Buffer (Shadow map)
Belle amélioration des Shadow-Buffers.
Au lieu de calculer deux Z-Buffer, on utilisera des identifiants : chaque objet possède un numéro unique l'identifiant. Depuis la source de lumière, on remplit un Id-Buffer, indiquant pour chaque point l'identifiant de l'objet affiché. Ensuite depuis la caméra principale, pour tous les points affichés, si l'identifiant de l'objet actuel est le même que celui retrouvé dans l'Id-Buffer, le point est éclairé, sinon il est ombré.
Cette méthode présente d'excellentes performances pour les moteurs 3D softwares, mais est peu adaptée aux accélérations matérielles: une carte 3D utilise des valeurs flottantes, où les id peuvent être stoqués trop approximativement.
Shadow Volumes
Cette méthode nécessite un Stencil Buffer. Ce buffer intégré aux cartes 3d permet d'autoriser point par point l'affichage.
|
- voici la scène à afficher. On l'affiche une fois dans le Z-Buffer.
Ensuite on configure le Z-Buffer pour permettre les comparaisons mais pas les écritures de Z.
- On trouve le contour de l'objet projetant une ombre.
Dans le Stencil Buffer, on met à 1 les pixels étant dans les polygones projetés en bleus (polygones infinis) et dont la coordonnée Z est inférieure ou égale à la valeur du Z-Buffer.
- On met à 0 les pixels dans les polygones verts dont la coordonnée Z est inférieure ou égale à celle du Z-Buffer.
- On dessine les pixels dont le Stencil-Buffer indique qu'ils sont dans l'ombre (1) ombrés, les pixels dont la valeur de Stencil est 0 éclairés.
Gros problème: quand la lumière projette une ombre sur la caméra, cette méthode ne marche pas. En effet les polygones projettés ne se recouvrent pas, donc des zones de l'écran non ombrées sont pourtant marquées comme telles.
|
Shadow volumes inversés (alias John Carmack's trick)
Cette technique est similaire aux shadow-volumes, si ce n'est qu'elle gère le cas spécial où la caméra est dans une zone d'ombre. Avec la technique précédente, les zones ombrées seraient éclairées et vice-versa.
|
- voici la scène à afficher. On l'affiche une fois dans le Z-Buffer.
Ensuite on configure le Z-Buffer pour permettre les comparaisons mais pas les écritures de Z.
- On trouve le contour de l'objet projetant une ombre.
Dans le Stencil Buffer, on met à 1 les pixels dans les polygones verts dont la coordonnée Z est supérieure ou égale à celle du Z-Buffer.
- On met à 0 les pixels étant dans les polygones projetés en bleus (polygones infinis) et dont la coordonnée Z est supérieure ou égale à la valeur du Z-Buffer.
- On dessine les pixels dont le Stencil-Buffer indique qu'ils sont dans l'ombre (1) ombrés, les pixels dont la valeur de Stencil est 0 éclairés.
|
Si on affiche les polygones latéraux comme expliqués précédemment, mais aussi les "caps" (le triangle original et son clone projeté), on affiche alors un volume fermé, et dans ce cas cette méthode d'ombrage est utilisable quelques soient la position de la caméra ou de la lumière.
Intérêts de chaque méthode :
Les Shadow-Buffers peuvent être réalisé même sur des petites configurations. Ils peuvent gérer les transparences alpha - ombres précises de chaque branche d'un arbre modélisé assez simplement avec de la transparence dans la texture par exemple. Mais leur rendu est assez pixelisé.
Les shadow-volumes sont parfaitement placés. C'est la méthode la plus réaliste. Mais ils ne savent pas gérer les transparences Alpha actuellement, et nécessite une grosse carte 3d pour afficher des scènes complexes. Obtenir des ombres "douces" (les bords de la zone ombrée reçoivent plus de lumière réfractée que le centre de la zone ombrée). avec les Shadow Volumes est assez lent. C'est la méthode la plus prometteuse pour l'instant.
Notez que dans le cas des shadow maps, on éclaire d'abord toute la scène puis on noircit les zones d'ombres, ce qui est physiquement incorrect. Dans le cas des shadow volumes, on peut n'éclairer que ce qui est "visible" par la lumière.
D'une projection d'ombre simple à un système d'ombrage complet
Bon vous savez maintenant déterminer si un pixel est dans une ombre ou non, reste à inclure cela dans votre moteur correctement. Bien sur on pourrait juste afficher un gros rectangle noir avec un test pour activer l'affichage si le stencil buffer est à un, mais ce serait trop simple, et ça ne gère pas tout les cas.
Une difficulté est d'ombrer correctement un objet non concave:
- Certaine partie de l'objet seront ombrées
- Certaine partie du décors autour seront "ombrées plusieurs fois": les ombres projetées par deux polygones différents se recouvriront
Une solution au deuxième problème est d'aplatir l'objet dans un plan en utilisant une matrice de projection. Gros inconvénient: l'objet ne peut alors plus s'ombrer lui-même.
Aussi je vous propose ma solution:
- Afficher la scène sans lumière (éclairage ambiant uniquement)
- Pour chaque lumière:
- Effacer le Stencil-Buffer
- Pour chaque objet
- Déterminer la position de la lumière dans l'espace de l'objet
- Calculer le shadow volume de l'objet
- Afficher la scène éclairée par la lumière là où le Stencil est à 0, en ajoutant la scène calculée à celle déjà présente (pour ajouter les lumières entre elles). Utilisez un blending avec la GL_CONSTANT_COLOR pour cela :
glBlendColor( lightcolor );
glBlendFunc( GL_CONSTANT_COLOR, GL_ONE );
Sur une GeForce 2, ça donne pour chaque lumière :
- 2 passes pour calculer le shadow volume (une pour les faces de devant, une pour les faces de derrière)
- 1 passe pour l'éclairage diffus : TU0 = DOT3 pour calculer l'éclairage, TU1 = MODULATE pour multiplier par la texture
- 1 passe pour l'éclairage spéculaire : TU0 = DOT3 pour calculer l'éclairage, TU1 = MODULATE pour multiplier par la texture
Inconvénient: tous les bits du Stencil Buffer peuvent être nécessaires. Ils ne pourront pas être utilisés pour afficher des miroirs par exemple.
Voir aussi
Projection d'ombre par Vector/Vertigo