Salut à tous !
Que pensez-vous de mes explications sur le Z-buffer, l’algorithme du Z-buffer et le Z-fighting ? Avez-vous des remarques à faire ? Trouvez-vous que je manque de précision ? Que j’ai fait des erreurs ?
Définitions : Z-buffer et algorithme du Z-buffer
Le Z-buffer est la structure de données utilisée par l’algorithme du Z-buffer, lequel permet de dessiner, depuis un espace à trois dimensions mais dans un espace à deux dimensions, tout ou partie d’un objet 3D par-dessus (donc en cachant) tout ou partie d’un autre objet 3D.
Le Z-buffer est en fait un tableau à deux dimensions, l’une correspondant à l’abscisse des pixels de l’écran (coordonnée “X”), l’autre à l’ordonnée des pixels de l’écran (coordonnée “Y”). Autrement dit, une case de ce tableau représente un pixel. La valeur de chaque case est, quant à elle, la profondeur du pixel (= coordonnée “Z”, d’où “Z-buffer”). Enfin, le Z-buffer a la même taille que le nombre de pixels de l’écran.
Algorithme du Z-buffer et événements qui déclenchent à la fois l’exécution de l’algorithme du Z-buffer et l’utilisation du Z-buffer par ce dernier
Algorithme
L’algorithme du Z-buffer fonctionne comme suit.
-
Initialisation de chaque case du Z-buffer à 0 ;
-
Un cube et une sphère doivent être dessinés à l’écran : ces deux objets 3D ne se touchent pas, mais le cube est placé devant la sphère et la cache ;
-
On donne à l’algorithme du Z-buffer la profondeur “A” de chaque pixel de chaque objet 3D à dessiner à l’écran (note : dans ce qui suit, “case adéquate” signifie “case de mêmes coordonnées X et Y que le pixel qui sera ou ne sera pas dessiné”) :
-
Si la valeur stockée dans le Z-buffer (laquelle est celle de la case adéquate) est égale à 0, alors le pixel sera dessiné et “A” remplacera l’ancienne valeur ;
-
Si “A” est strictement inférieure à la valeur stockée dans le Z-buffer (laquelle est celle de la case adéquate), alors ce pixel sera dessiné et “A” remplacera l’ancienne valeur ;
-
Sinon, si “A” est strictement supérieure à la valeur stockée dans le Z-buffer (laquelle est celle de la case adéquate), alors ce pixel ne sera pas dessiné et aucun remplacement de valeur n’a lieu.
-
A l’issue de ce traitement, pour tout couple de pixels qui sont superposés, seul celui de profondeur moindre est dessiné, reproduisant ainsi la notion de “profondeur” de la vraie vie et de l’espace en trois dimensions, dans un espace en deux dimensions.
-
En conclusion, seul le cube sera dessiné.
-
On notera par ailleurs qu’on obtiendrait un résultat correct et correspondant bien à ce qu’on veut, si une partie du cube cachait une partie de la sphère tout en ayant un dépassement de l’un de ces deux objets sur l’autre (exemple avec des triangles : http://raphaello.univ-fcomte.fr/IG/ZBuffer/Images/ZBuffer3-01.gif).
Evénements déclencheurs
L’exécution de cet algorithme a lieu suite à plusieurs événements :
-
Dans une visualisation 2D d’une scène 3D, chaque fois que la caméra se déplace d’une unité (en reculant par-rapport aux objets ou bien en avançant) ;
-
Dans une visualisation 2D d’une animation 3D, chaque fois qu’une milliseconde s’écoule.
Problème : le Z-fighting
Définition
On notera l’existence d’un problème nommé “Z-fighting” si deux objets 3D se touchent et qu’une partie de leurs pixels ont les mêmes coordonnées X, Y et Z (en particulier, on constatera qu’ils ont la même profondeur : coordonnée “Z”).
Dès lors, en considérant tout couple de pixels qui se chevauchent, l’algorithme dessinera le pixel de l’un de ces deux objets (lequel est choisi aléatoirement), puis à l’événement suivant (après une milliseconde ou bien si la caméra avance d’une unité), ce choix aléatoire se fera de nouveau : en résulte un clignotement de cette partie des deux objets 3D, clignotement visible par l’utilisateur de l’ordinateur.
Le Z-fighting a aussi lieu quand les cases du Z-buffer manquent de précision… !
Si deux objets 3D, un cube et une sphère, possèdent des pixels qui ont les mêmes coordonnées X et Y, mais une profondeur, notée “Z”, très légèrement différente, l’utilisateur de l’ordinateur, qui les visualise dans l’espace en trois dimensions, pourra constater que ces deux objets ne se touchent pas : le Z-fighting ne devrait donc pas apparaître. Or, chaque case du Z-buffer n’accepte des valeurs que plus ou moins précises. Imaginons par exemple que le pixel du cube (1 ; 1) ait une Z = 2.6, que le pixel de la sphère (1 ; 1) ait une Z = 2.8, que chaque case du Z-buffer n’accepte que des valeurs comme : {2, 3, 4…} et que la case du Z-buffer (1 ; 1) vaut 0 : que se passera-t-il d’après l’algorithme précédemment donné ? Un 0 est présent dans la case adéquate, donc le pixel de l’objet 3D à dessiner en 2D sera forcément dessiné et mis dans la case du Z-buffer ; sauf que cette dernière ne peut contenir que des valeurs comme {2, 3, 4…} : ainsi 2.6 sera-t-il converti en 3 (il en serait de même pour 2.8). Puis lorsqu’un prochain événement déclencheur aura lieu, la comparaison retournera de nouveau VRAI, il y aura de nouveau conversion et de nouveau remplacement de valeur. Donc le Z-fighting aura lieu. Sans conversion (due au manque de précision et donc de bits de chaque case du Z-buffer), il n’y aurait en revanche pas eu de Z-fighting.
Exemple avec Z-fighting
Chaque case du Z-buffer peut contenir des valeurs comme : {2, 3, 4…}
-
Cube(1 ; 1 ; 2.6) - Sphère (1; 1; 2.8) - Z-buffer (1 ; 1 ; 0)
-
Il existe un 0 donc Z-buffer (1 ; 1 ; 3) (Cube(1 ; 1) dessiné)
-
2.8 < 3 donc Z-buffer(1 ; 1 ; 3) (Sphère(1 ; 1) dessiné)
-
2.6 < 3 donc Z-buffer(1 ; 1 ; 3) (Cube(1 ; 1) dessiné)
Et à chaque événement déclencheur, ça recommence… : présence du clignotement (= présence du Z-fighting).
Exemple sans Z-fighting
Chaque case du Z-buffer a suffisamment de précision.
-
Cube(1 ; 1 ; 2.6) - Sphère (1; 1; 2.8) - Z-buffer (1 ; 1 ; 0)
-
Il existe un 0 donc Z-buffer (1 ; 1 ; 2.6) (Cube(1 ; 1) dessiné)
-
2.8 > 2.6 donc Z-buffer(1 ; 1 ; 2.6) (Sphère(1 ; 1) non-dessiné)
Et à chaque événement déclencheur, ça recommence… : absence de clignotement car le pixel du cube est dessiné, et jamais celui de la sphère (= absence de Z-fighting).