Je dois écrire mon propre logiciel de rastérisation 3D, et jusqu'à présent, je suis capable de projeter mon modèle 3D composé de triangles dans un espace 2D:
Je fais pivoter, traduire et projeter mes points pour obtenir une représentation spatiale 2D de chaque triangle. Ensuite, je prends les 3 points du triangle et j'implémente l'algorithme de la ligne de balayage (en utilisant l'interpolation linéaire) pour trouver tous les points [x] [y] le long des bords (gauche et droite) des triangles, afin de pouvoir balayer le triangle horizontalement, ligne par ligne, et remplissez-la de pixels.
Cela marche. Sauf que je dois également implémenter la mise en mémoire tampon z. Cela signifie que connaissant les coordonnées z pivotées et traduites des 3 sommets du triangle, je dois interpoler la coordonnée z pour tous les autres points que je trouve avec mon algorithme de ligne de balayage.
Le concept semble assez clair, je trouve d'abord Za et Zb avec ces calculs:
var Z_Slope = (bottom_point_z - top_point_z) / (bottom_point_y - top_point_y);
var Za = top_point_z + ((current_point_y - top_point_y) * Z_Slope);
Puis pour chaque Zp je fais la même interpolation horizontalement:
var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);
Et si le z actuel est plus proche du spectateur que le z précédent à cet index ALORS écrivez la couleur dans le tampon de couleur ET écrivez le nouveau z dans le tampon z. (mon système de coordonnées est x: gauche -> droite; y: haut -> bas; z: votre visage -> écran d'ordinateur;)
Le problème, c'est que ça se détraque. Le projet est là et si vous sélectionnez le bouton radio "Z-Buffered", vous verrez les résultats ... ( notez que j'utilise l'algorithme du peintre (-seulement- pour dessiner le filaire) en mode "Z-Buffered") à des fins de débogage )
PS: J'ai lu ici que vous devez transformer les z en leurs inverses (ce qui signifie z = 1/z
) avant d'interpoler. J'ai essayé, et il semble qu'il n'y ait aucun changement. Qu'est-ce que je rate? (quelqu'un pourrait-il clarifier précisément où vous devez transformer z en 1 / z et où (si) le retourner?)
[MODIFIER] Voici quelques données sur les valeurs z maximales et minimales que j'obtiens:
max z: 1; min z: -1; //<-- obvious, original z of the vertices of the triangles
max z: 7.197753398761272; min z: 3.791703256899924; //<-- z of the points that were drawn to screen (you know, after rotation, translation), by the scanline with zbuffer, gotten with interpolation but not 1/z.
max z: 0.2649908532179404; min z: 0.13849507306889008;//<-- same as above except I interpolated 1/z instead of z.
//yes, I am aware that changing z to 1/z means flipping the comparison in the zBuffer check. otherwise nothing gets drawn.
Avant d'entrer dans un débogage minutieux, quelqu'un peut-il confirmer que mon concept jusqu'à présent est correct?
[EDIT2]
J'ai résolu le z-buffering. Il s'est avéré que l'ordre de dessin n'était pas du tout foiré. Les coordonnées z étaient calculées correctement.
Le problème était, dans le but d'augmenter ma fréquence d'images, je dessinais des boîtes 4px / 4px, tous les 4 pixels, au lieu de pixels réels à l'écran. Je dessinais donc 16 pixels par pixel, mais en vérifiant le tampon z pour un seul d'entre eux. Je suis un tel boob.
TL / DR: La question se pose toujours: comment / pourquoi / quand devez-vous utiliser l'inverse de Z (comme dans 1 / z) au lieu de Z? Parce qu'en ce moment, tout fonctionne dans les deux sens. (il n'y a pas de différence notable).
la source
Réponses:
Réponse rapide: Z n'est pas une fonction linéaire de (X ', Y'), mais 1 / Z l'est. Puisque vous interpolez linéairement, vous obtenez des résultats corrects pour 1 / Z, mais pas pour Z.
Vous ne le remarquez pas car tant que la comparaison entre Z1 et Z2 est correcte, le zbuffer fera la bonne chose, même si les deux valeurs sont fausses. Vous remarquerez certainement lorsque vous ajoutez un mappage de texture (et pour répondre à la question, vous devrez alors: interpoler 1 / Z, U / Z et V / Z, et reconstruire U et V à partir de ces valeurs: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Vous me remercierez plus tard)
Un exemple. Obtenez un morceau de papier. Vue de haut en bas, oubliez donc la coordonnée Y. X est l'axe horizontal, Z est l'axe vertical, la caméra est à (0, 0), le plan de projection est z = 1.
Considérez les points A (-2, 2) et B (2, 4). Le point milieu M du segment AB est (0, 3). Jusqu'ici tout va bien.
Vous projetez A dans A ': X' = X / Z = -1, donc A 'est (-1, 1). De même, B 'est (0,5, 1). Mais notez que la projection de M est (0, 1), ce qui n'est PAS le milieu de A'B '. Pourquoi? Parce que la moitié droite du segment est plus éloignée de la caméra que la moitié gauche, elle semble donc plus petite.
Alors, que se passe-t-il si vous essayez de calculer le Z de M 'en utilisant une interpolation linéaire? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, donc pour M 'où X' = 0, le Z interpolé linéairement est zA + (dz / dx) (x - xA) = 2 + (2 / 1,5) (0 - -1) = 2 + 1,333 = 3,3333 - PAS 3!
Pourquoi? Parce que pour chaque pas dans la direction X ', vous ne déplacez pas la même quantité dans la direction Z (ou, en d'autres termes, Z n'est pas une fonction linéaire de X'). Pourquoi? Parce que plus vous allez à droite, plus le segment est éloigné de la caméra, donc un pixel représente une distance plus longue dans l'espace.
Enfin, que se passe-t-il si vous interpolez 1 / Z à la place? Vous calculez d'abord 1 / Z à A et B: 0,5 et 0,25 respectivement. Ensuite, vous interpolez: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, donc à X '= 0 vous calculez 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Mais c'est 1 / Z, donc la valeur de Z est ... exactement, 3. Comme il se doit.
Oui, les mathématiques sont géniales.
la source