
Programmation : Initiation à la 3D

Et oui un article d'initiation à la 3D! D'ou son nom, me direz vous, mais
il est vrai qu'avec un titre aussi court il est difficile d'aborder d'autre
sujet (que ceux qui trouverons cette phrase inutile et parfaitement débile
aurons raison, mais il faut bien que je m'échauffe les articulations
digitales!). Bon commençons par le commencement : il était une fois un
peintre (je ne sais pas qui a commencer, mais ça date de longtemps) qui
désirait représenter une scène de la vie courante sur un plan (imaginez une
toile de peinture par exemple) de manière réaliste. Comment projeter un
univers tri-dimensionnel (celui dans lequel nous évoluons) dans un univers
bi-dimensionnel (en l'occurrence la toile). Les peintres, puis les
mathématiciens ont élaboré plusieurs techniques de projections :
- projection cavalière
- projection centrale
- projection en perspective
- projection parallèle
Nous allons voir seulement deux types de projections : la projection
parallèle et la projection centrale. Je vais commencer par vous dire comment
on représente un point en 3 dimension. Un point se représente par 3
coordonnées qui sont les coordonnées en x, y et z. Il existe d'autre méthode
de représentation de point en 3 dimensions, notamment la représentation
sphérique (deux angles et une distances). Bon voici un petit schéma pour
clarifier les idées :
^ y
|
|
Valeur en y -> |----* <- ceci est mon point de coordonnées (x,y,z)
| |
| | +-- valeur en x
Origine -> *----+--->
/ |/ x
/------*
/ ^
/ +--- valeur en z
\/ z
Nous avons donc manière de caractériser un point dans
l'espace, maintenant il nous reste à savoir comment projeter un
point de l'espace sur notre plan qui est notre écran.
Commençons par :
La projection parallèle
Le principe de la projection est le suivant :
* <- point projeté
/| ^ x
/ | |
/ | |
plan de projection ->/ | |
point image -> /| | |
/ | | |
/ | | |
Point de vue -> *-------+ |
<--><-----+ <- origine du repère
d z
Une première remarque : avec cette projection, on regarde le
centre du monde (coordonnées (0,0,0)). La formule pour projeter un
point résulte de la propriété des triangles
semblables. Ainsi le x (xe) de l'écran s'écrit :
X
xe = - * d
Z
De même :
Y
xe = - × d
Z
La distance d est à fixer. Plus elle grande plus l'objet est petit
et pas trop déformé, par contre plus elle est petite, plus
l'objet est grand et déformé. A vous de voir pour fixer
cette valeur. Dans le programme d'exemple fourni (3d_paral.s), on peut la
fixer grâce à la constante d (elle porte bien son nom!)
définit en début de programme. Dans ce programme, j'ai choisi
de représenter les nombres en virgule fixe (8 bits de partie
entière, 8 bits de partie décimale). Ainsi les
coordonnées des points dans l'espace sont stockées eux
aussi en virgule fixe, sur 3 mots consécutifs (x, y, z) de 16
bits. Pour représenter l'objet (pyramide à base carré
tronqué) en fil de fer (uniquement les arêtes de l'objet),
chaque arête est définie par les deux points qui se suive. Je
réutilise la routine de tracer de ligne publiée dans un
précédent numéro, donc je ne m'étendrai pas
dessus. La boucle principale se charge simplement de parcourir la liste des
arêtes, de les projeter sur l'écran et de les afficher :
rien de bien compliqué. On remarque que cette projection n'est pas
parfaite, elle est même presque pas réaliste! Mais
heureusement il y a une autre projection au menu de cet article :
La projection centrale
Pour cette projection, je n'ai pas trouver d'explication pour la formule,
mais peut-être qu'elle résulte d'expérience empirique. Bon voici la formule
de projection :
X
xe = -
Q
avec
Z
Q = 1 - et P une constante représentant le point de fuite.
P
de même pour y :
Y
y = -
Q
Le petit programme d'exemple se nomme 3d_centr.s. On reprend les
mêmes conventions que pour 3d_paral.s : nombre en virgule fixe,
représentation des arêtes, ... Et il y a une constante P
pour définir l'inverse du point de fuite (représenté
en virgule fixe). Il n'y a pas grand chose à ajouter sinon que
cette projection est nettement plus réaliste. Bon maintenant que
l'on sait projeter notre monde en 3 dimension, il faudrait bien qu'il
bouge un peu! Allez, commençons par les :
Translations
Pour translater, c'est à dire déplacer sans faire tourner,
un point dans l'espace (et même dans le plan, à une dimension
près), il suffit d'ajouter à ces coordonnées les
composantes du vecteur translations. Je m'explique : imaginez que vous
souhaitez déplacer un point vers les x croissants de 10 pas, et
il faut translater votre point avec un vecteur déplacement de (10,
0, 0) (10 pas en x, 0 pas en y, 0 pas en z) (un vecteur représente
donc une direction). Donc soit (dx, dy, dz) le vecteur
déplacement et (x, y, z) le point de départ, alors le point
d'arrivé de la translation est de (x + dx, y + dy, z + dz)! Pour
translater un objet, il suffit de translater tous les points qui le
compose du même vecteur déplacement. Aucun programme d'exemple
n'est fourni, mais je pense que vous y arriverez tout seul! Une
autre manière de faire déplacer un objet, c'est de le faire
tourner :
Rotations
Pour la translation, on pouvait parler de translation suivant les 3
axes de notre espace, et bien pour les rotations, on peut parler de 3
axes de rotations. Pour être clair, je vais vous montrer comment
l'écran représente notre espace :
*------> x Il y a donc l'axe z qui va vers nous.
/|
/ |
/ |
/ |
z \/ \/ y
Ainsi faire une rotation autour de l'axe des x, c'est comme si votre objet
jouait à la balançoire. De même pour l'axe z, mais dans un autre sens
(perpendiculaire au sens de x). Par contre une rotation autour de l'axe y,
c'est plutôt comme ouvrir une porte. Maintenant les formules, commençons par
la rotation suivant l'axe z (c'est à dire une rotation dans le plan (x, y)).
Voici un petit schéma :
* point de départ On remarque que la distance par
/ rapport à l'origine ne change pas
/
/\ <- angle de rotation theta
*--|------> x
|\/
| \
| \
| * point d'arrivée
|
\/ y
Et voici les formules de rotations autour de l'axe z :
x_a = x_d*cos(theta) + y_d*sin(theta)
y_a = -x_d*sin(theta) + y_d*cos(theta)
avec (x_d, y_d) les coordonnées du point de départ (la coordonnée en z est
inchangée), (x_a, y_a) les coordonnées du point d'arrivée et theta l'angle
de rotation. On fait la même chose pour les 2 autres axes et l'on obtient
comme formule générale :
x_a = x_d*(sin(rho)*sin(theta)*sin(gamma) + cos(rho)*cos(gamma))
+ y_d*(cos(theta)*sin(rho))
+ z_d*(cos(gamma)*sin(rho)*sin(theta) - cos(rho)*sin(gamma))
y_a = x_d*(cos(rho)*sin(theta)*sin(gamma) - cos(gamma)*cos(rho))
+ y_d*(cos(rho)*cos(theta))
+ z_d*(cos(rho)*cos(gamma)*sin(theta) + sin(rho)*sin(gamma))
z_a = x_d*(cos(gamma)*sin(rho)*sin(theta) - cos(rho)*sin(gamma))
- y_d*sin(theta)
+ z_d*(cos(gamma)*cos(theta))
Avec rho, l'angle de rotation suivant l'axe x et gamma l'angle de rotation
suivant l'axe y. Dans le programme d'exemple (3d_rotat.s), les angles de
rotation sont représenté sur 16 bits, avec les 8 bits de poids de fort qui
contient la partie entière de l'angle. De plus, au lieu de représenter un
angle entre 0 et 360 degrés, on choisit généralement de représenter un angle
entre 0 et 256, ce qui permet de faciliter les calculs d'ajout d'un angle.
Une autre astuce est utilisée pour calculer les sinus et autre cosinus
nécessaire à la rotation : on précalcule les 256 valeurs nécessaires, et on
accède à cette table grâce au mode d'adressage indirect indexé, ce qui
permet d'obtenir la valeur d'un sinus ou d'un cosinus en une instruction!
Les calculs dans le programme se font toujours suivant la représentation 8
bits/8bits, et l'on précalcule les coefficients de chaque coordonnées avant
le traçage de l'objet. Les angles de rotation varient suivant les constantes
: offset_ang_x, offset_ang_y et offset_ang_z, donc je vous conseille pour
bien voir comme tourne notre petit monde, de fixer à 0 deux pas de rotation
et de laisser le 3ième pour bien voir comment ça tourne! Bon je crois qu'il
n'y a pas grand chose à rajouter à ce premier article sur la 3D, sinon
d'aller voir l'article sur le shading !
Golio Junior