* * *
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 :


 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