Tutoriaux: 3D - Théories et mathématiques

Introduction

Petite piqure de rappel, le calcul en 3 dimension n’est pas plus compliqué que le calcul en 2 (ou en 1) dimension, tous le monde est capable d'additionner et de multiplier des nombres...

Le calcul en 1 dimension est en fait le calcul d’algèbre classique, que vous faites tous les jours en faisant vos courses par exemple.

Le calcul en 2 dimension est de la géométrie simple, rappelez vous les translations, homotétites, rotations, (bon ce dernier point est souvent abordé en cours de trigonométrie), mais en réalité, le calcul 3D n’est jamais que l’extension des deux premiers espaces, ou si on est « positif », le calcul en 2 dimensions ou algébrique n’est jamais qu’une simplification du calcul en 3D.

Malheuresement, pour comprendre et "masteriser" la 3d sur ordinateur, un gros bloc de théorie mathématique est a lire, je vais essayer de le faire aussi court mais complet que possible...

Systeme de références

Tout est une question de références, il existes trois grands systemes pour décrire un espace en trois dimensions, il existe trois grands « système » de représentation mathématique.

Le systeme cartésien

Sans doute le plus simple a comprendre car le plus proche de notre mode « mathématique ».

Une coordonnée est exprimée « simplement » avec avec trois coordonnées : x, y, z

Radian vs Degré

Très souvent, dans les fonctions de trigonométriques informatiques, les angles sont exprimés en radian, et non en degrés (d'angle).

un radian est une valeur réel, alors qu'un degré d'angle est généralement un entier.

Notre cerveau a plus facile a manipuler la notion même de degré d'angle, car les valeurs nous sont plus communes : 0°, 90° pour un angle droit, 180°, etc...

pourtant, il est impératif d'utiliser des radians lorsqu'on fait des calculs trigonométriques, nous allons directement voir la relation entre radian et degré d'angle (180 degré d'angle, correspondent à Π radian)

Π radian = 180 degré

Donc 1 radian vaut +/- 57.2958 degré d'angle (suivant la valeur de Π pour effectuer la conversion) pour connaitre la valeur (exacte) d'un radian, il suffit de:

1 radian = 180/Π degré

Les formules de conversion sont donc les suivantes:

degré -> radian

x radian = angle * (Π/180)

radian -> degré

x degré = rad * (180/Π)

le systeme sphérique

C’est le systeme qui est utilisé pour la géolocalisation ou la définition d’objets céleste ; on parle de coordonnées angulaire (appelées latitude et longitude dans la géolocalisation) et d’un rayon (l’altitude pour ce qui est de la géolocalisaion, ou distance dans le cas d’objets céleste) : 

Une coordonnée est exprimée avec un rayon r et deux angles : r, θ, φ.

le systeme cylindrique

Ce systeme est le moins répendu, et souvent utilisé pour pour l’étude de mouvements hélicoïdaux ou en rotation autour d’un axe.

Une coordonnée est exprimée via deux angles : ρ, φ, et une distance : z

Conversions entre systèmes de références

Sphérique → Cartésien

x = r * sin (θ) * cos (φ)
y = r * sin (θ) * cos (φ)
z = r * cos (θ)

Cylindrique → Cartésien

x = ρ cos( φ) 
y = ρ sin( φ)
z = z

Calculs et matrices

En calcul 3D, le calcul matriciel la méthode la plus simple pour résoudre les transformations que l'on veut appliquer.

De plus, les opperations nécéssaires se limitent surtout à deux oppérations majeurs : la multiplication des matrices (produit matriciel) et l'addition de matrices.

Pour additionner 2 matrices, il sagit de faire la somme de leurs membres:

|a1 b1|   |c1 d1|   |(a1+c1) (b1+d1)|
|a2 b2| + |c2 d2| = |(a2+c2) (b2+d2)|

Pour la multiplication, on utilisera la technique "LICOL" (Ligne Colonne), on multiplie chaque ligne par la colonne.

|a1 b1|   |c1 d1|   |(a1*c1)+(b1*c2) (a1*d1)+(b1*d2)|
|a2 b2| * |c2 d2| = |(a2*c1)+(b2*c2) (a2*d1)+(b2*d2)|

Note sur la multiplication par 1

Dans un calcul simple, quand on multiplie n'importe quelle valeur par 1 on obtient toujours la valeur d'origine :

x' = x * 1 

et donc x' = x

En calcul matriciel , la valeur '1' se présente comme ceci:

|1 0|
|0 1|

Pour des questions pratiques, je ne me pencherais que sur le systeme cartésien, plus lisible et surtout plus commun, mais sachez qu’il est tout a fait possible de convertir une carte de la galaxie (qui est trouvable en coordonnées sphérique) dans un systeme cartésien: 

(x,y,z)

et, ainsi la multiplication d'une matrice par '1' se présente comme ceci : 

|x'|   |x|   |1 0 0|
|y'| = |y| * |0 1 0|
|z'|   |z|   |0 0 1|

Comme tous les langages de programmations n'incluent pas nativement le support de calcul matriciel, il convient de traduire ce produit matriciel en fonction simple (ou en une dimension) et de le faire pour chaque membre de la coordonnée que l'on veut calculer, vu la méthode LICOL que nous avons vu plus haut, cela devient donc:

x' = (x*1) + (y*0) + (z*0)
y' = (x*0) + (y*1) + (z*0)
z' = (x*0) + (y*0) + (z*1)

Ce qui dans ce cas précis peut s'optimiser par l'application des formules suivantes:

x' = x * 1
y' = y * 1
z' = z * 1

Coordonnées homogènes

Toutefois, pour pouvoir correctement appliquer certaines transformations (notament la translation), dans certaines litératures, on rends homogènes les matrices, en ajoutant une 4ème valeur w, qui doit TOUJOURS être mise à 1, on parle alors de matrices homogènes :

|1 0 0 0|
|0 1 0 0|
|0 0 1 0|
|0 0 0 1|

Ce qui peut se traduire simplement : 

x' = (x*1) + (y*0) + (z*0) + (w*0)
y' = (x*0) + (y*1) + (z*0) + (w*0)
z' = (x*0) + (y*0) + (z*1) + (w*0)
w' = (x*0) + (y*0) + (z*0) + (w*1)

Ou encore en mode "optimisé", afin de démontré que les matrices, c'est simple : 

x' = x * 1
y' = y * 1
z' = z * 1
w' = w * 1

En résumé, un produit matriciel d'une matrice point "P" avec une matrice de transformation "T" s'écrit comme ceci:

|x'| = |x|   |xa xb xc xd|
|y'| = |y|   |ya yb yc yd|
|z'| = |z| * |za zb zc zd|
|w'| = |w|   |wa wb wc wd|

Et se résouds avec les équations suivantes:

x' = (x*xa) + (y*xb) + (z*zb) + (w*xd)
y' = (x*ya) + (y*yb) + (z*yb) + (w*yd)
z' = (x*za) + (y*zb) + (z*zb) + (w*zd)
w' = (w*wa) + (w*wb) + (w*wb) + (w*wd)

On constate immédiatement que l'ajout de la coordonnée "w" a dans cette serie d'équations peut influancé grandement (ou pas) les résultats.

Transformations

Tout ensemble de transformations géométrique doit se faire dans un certain ordre qu'il convient de décider à l'avance.

Une rotation suivit d'une translation et d'un changement d'échelle, dans cet ordre, ne donneront pas le même résultat qu'un changement d'échelle suivit d'une rotation et enfin de la translation... 

Sans compter qu'il existe des transformations "plus fun" comme le scissaillement ou le mirroir suivant un plan...

Dans cette je ne fournirais que les matrices (et leurs traductions sans optimisation).

Les changement d'échelle

Sans doute la matrice la plus simple, la matrice de transformation d'échelle:

|ex  0  0 0|
| 0 ey  0 0|
| 0  0 ez 0|
| 0  0  0 1|

On peut soit avoir une même echelle pour tout les axes : ex = ey = ez = echelle ou faire un changement d'échelle propre à chaque axes (ce qui peut amener des résultats rigolo).

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x*ex) + (y* 0) + (z* 0) + (w* 0)
y' = (x* 0) + (y*ey) + (z* 0) + (w* 0)
z' = (x* 0) + (y* 0) + (z*ez) + (w* 0)
w' = (x* 0) + (y* 0) + (z* 0) + (w* 1)

Translation

Si il existe une transformation souvent sous estimée, c'est bien celle-ci, le déplacement d'un point dans l'espace:

| 1  0  0 0|
| 0  1  0 0|
| 0  0  1 0|
|tx ty tz 1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* 1) + (y* 0) + (z* 0) + (w*tx)
y' = (x* 0) + (y* 1) + (z* 0) + (w*ty)
z' = (x* 0) + (y* 0) + (z* 1) + (w*tz)
w' = (x*tx) + (y*ty) + (z*tz) + (w* 1)

Rappelez vous que w = 1, et donc nos formules peuvent se traduire par:

x' = x+tx
y' = y+ty
z' = z+tz

Réflection par rapport à un axe

Avec cette transformation, il est facile d'avoir le mirroir d'un point par rapport à un des axes, cela peut servir soit pour créer un double de l'objet, soit pour le représenter "en mode mirroir"... (a vous de voir si vous dupliquez ou non le point)

Le plan de "mirroir" est toujours défini par rapport a deux axes:

Effet mirroir par rapport au plan x/y (chaque coordonnée 'z' sera inversée):

| 1  0  0  0|
| 0  1  0  0|
| 0  0 -1  0|
| 0  0  0  1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* 1) + (y* 0) + (z* 0) + (w* 0)
y' = (x* 0) + (y* 1) + (z* 0) + (w* 0)
z' = (x* 0) * (y* 0) * (z*-1) + (w* 0)
w' = (x* 0) + (y* 0) + (z* 0) + (w* 1)

Effet mirroir par rapport au plan x/z (chaque coordonnée 'y' sera inversée):

| 1  0  0  0|
| 0 -1  0  0|
| 0  0  1  0|
| 0  0  0  1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* 1) + (y* 0) + (z* 0) + (w* 0)
y' = (x* 0) + (y*-1) + (z* 0) + (w* 0)
z' = (x* 0) * (y* 0) * (z* 1) + (w* 0)
w' = (x* 0) + (y* 0) + (z* 0) + (w* 1)

Effet mirroir par rapport au plan y/z (chaque coordonnée 'x' sera inversée):

|-1  0  0  0|
| 0  1  0  0|
| 0  0  1  0|
| 0  0  0  1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x*-1) + (y* 0) + (z* 0) + (w* 0)
y' = (x* 0) + (y* 1) + (z* 0) + (w* 0)
z' = (x* 0) * (y* 0) * (z* 1) + (w* 0)
w' = (x* 0) + (y* 0) + (z* 0) + (w* 1)

Rotation autour des axes de références

C'est dans les rotations que les choses se compliquent, et que la trigonométrie commence, c'est aussi dans les rotations que l'on prends vraiment la troisième dimension "dans les yeux"; Nous allons différencier les 3 axes, mais en réalité les matrices sont particulièrement similaires...

Autour de l'axe Y, avec un angle de ay °

| cos(ay)      0 -sin(ay)       0|
|       0      1        0       0|
| sin(ay)      0  cos(ay)       0|
|       0      0        0       1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* cos(ay)) + (y*       0) + (z*-sin(ay)) + (w* 0)
y' = (x*       0) + (y*       1) + (z*       0) + (w* 0)
z' = (x* sin(ay)) + (y*       0) + (z* cos(ay)) + (w* 0)
w' = (x*       0) + (y*       0) + (z*       0) + (w* 1)

Autour de l'axe X, avec un angle de ax °

|      1        0        0 0|
|      0  cos(ax)  sin(ax) 0|
|      0 -sin(ax)  cos(ax) 0|
|      0        0        0 1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* cos(ax)) + (y* 0) + (z*-sin(ay)) + (w* 0)
y' = (x*       0) + (y* 1) + (z*       0) + (w* 0)
z' = (x* sin(ax)) + (y* 0) + (z* cos(ay)) + (w* 0)
w' = (x*       0) + (y* 0) + (z*       0) + (w* 1)

Autour de l'axe Z, avec un angle de az °

| cos(az) sin(az) 0 0|
|-sin(az) cos(az) 0 0|
|       0       0 1 0|
|       0       0 0 1|

Voici les formules a appliquer si l'on désire transformer un point particulier:

x' = (x* cos(az)) + (y* sin(az)) + (z* 0) + (w* 0)
y' = (x*-sin(az)) + (y* cos(az)) + (z* 0) + (w* 0)
z' = (x*       0) + (y*       0) + (z* 1) + (w* 0)
w' = (x*       0) + (y*       0) + (z* 0) + (w* 1)

Conclusions temporaires

Avec ces outils, il est possible de modifier un point (ou des points) dans l'espace, et de le(s) placer avec précision où l'on veut (on peut aussi appliquer une transformation puis une autre dans n'importe quel ordre), et créer a chaque fois un nouveau point... 

Ces transformations modifient la positions des points dans l'univers; nous y reviendrons plus tard...

Il est important de noter que ces transformations sont des outils aidant à créer d'autre points, ainsi si l'on dessine une forme (avec des points) sur un plan X/Y, il est très facile de dupliquer ces points en faisant 36 rotations de 10° à chaque fois, et ainsi de créer un objet en 3D "tourné" a partir d'un simple profile.

Dans la suite de cet article, je resterais (sauf exception) sur des matrices "classiques" à 3 valeurs (x,y,z).

Projection sur l'écran

Il nous reste à aborder le passage de la 3D à la 2D (pour nos "simple" écrans)...

Nous avons transformé des points (qui peuvent former des objets) dans un espace à 3 dimensions, avec un point remarquable (0,0,0) qui est le centre de l'univers cartésiens ainsi décrit. 

Par défaut, si l'on applique les formules de projection ci-dessous, l'observateur sera un point "remarquable" (et immobile) situé en (0,0,0) et rqui egarderarait vers le fond (0°,0°,0°) ... :D

Projection sans distance, projection orthonormée (projection du fénéant)

Si l'on désire représenter un point 3D en 2D, après transformation(s), on peut simplement ignorer la coordonnées 'Z'

x' = x
y' = y

Centrer l'écran

Sachant qu'un écran informatique peut afficher des points de 0,0 à sizex, sizey et que les valeurs de x' et y' peuvent quand à elles varier t entre -∞ et +∞, il convient de centrer le 0,0 sur l'écran et d'éliminer les valeurs "hors écran"

x' = x' - (sizex/2)
y' = y' - (sizey/2) 

if ((x'>=0) and (x'<sizex) and (y'>=0) and (y<sizey))
  draw_pixel(x',y',1)

Projection Simple dit aussi Projection du pauvre

Si l'on désire représenter un point 3D en 2D, après transformation(s), on simuler l'éloignement (par rapport à l'observateur) en faisant un rapport sur la valeur z, après transformation:

x' = x / z
y' = y / z

Cette projection transforme un point 3D en 2D en tenant compte de l'éloignement, plus le Z est grand, plus les points projetés se "rapprochent" les un des autres autour du 0,0, et donc donneront l'ilusion d'etre "plus loin".

C'est la technique qui est utilisée en peinture (technique du "point de fuite") pour donner un sentiment de "profondeur" à l'image, on parle alors de perspective.

Dans cette projection, Il convient toutefois de prendre en compte le risque d'avoir un z nulle avant d'appliquer la projection.

Cas du 'Z' nulle

On peut soit ignorer les points qui sont sur l'origine, soit les afficher en utilisant la projection du fénéant, je conseille de quand meme les afficher.

if (z==0)
{
  x' = x
  y' = y
}
else
{
  x' = x / z
  y' = y / z
}

Cette gestion ne vous empeches pas de quand meme centrer votre 0,0 et les "hors-écran", donc alourdis un peu la projection.

Projection sur l'écran

Considérons un instant l'écran (de projection) comme pouvant se situé entre l'observateur et l'objet observé, il convient de prendre en compte cette différence, afin notament de gérer l'effet de focale (et éventuellement de traiter le centrage)...

la projection sera dès lors calculée avec les formules suivantes

x' = x*(ez/z) + ex
y' = y*(ez/z) + ey

Si l'on considère que ex = -(sizex/2) et ey = -(sizey/2), et que ez=1, alors nous retombons sur la formule de la projection du pauvre... qui tiens nativement compte du centrage de l'écran...

Caméra et point de vue

Dans les projections précédentes, nous avons toujours fait bouger les points de l'univers autours du 0,0,0 et non fait bouger un observateur dans cet univers, la logique voudrait que ce soit en effet l'observateur qui se déplace et non l'univers.

Si l'on désire faire entrer la notion d'observateur(caméra)/point de vue, il convient d'inclure ce point (et là ou il regarde) dans les calculs de transformation/projection et d'appliquer aux points de l'univers une transformation spéciale.

Si l'on désire appliquer les transformations en prenant en compte la position de l'observateur, il convient d'appliquer la transformation de la caméra, cette transformation inclu une rotation autour de l'axe Z, de l'axe Y et de l'axe X de chaque point par rapport à la position de l'observateur/caméra et à son orientation (du regard).

Soit:

x,y,z : coordonnées cartésienne du point a transformer
ox,oy,oz : coordonnées cartésienne, position de l'oeil dans l'espace.
ax,ay,az : angles de vue , ces angles définissent la direction du regard à partir du point de l'observateur (ox,oy,oz).

Alors la matrice de transformation devient

|x'|   | 1        0       0|   | cos(ay) 0 -sin(ay)|   | cos(az) sin(az) 0|    |x|   |ox|
|y'| = | 0  cos(ax) sin(ax)| * |       0          1| * |-sin(az) cos(az) 0| * (|y| - |oy|)
|z'|   | 0 -sin(ax) cos(ax)|   | sin(ay) 0  cos(ay)|   |       0       0 1|    |z|   |oz|

Note: 

Dans l'introduction à la projection, j'ai évoqué l'observateur comme étant un point remarquable du système localisé en (0,0,0), et ayant une direction de regard a angle nul (0°,0°,0°)

Si l'on n'a pas d'angle de vue (ax=ay=az=0), les matrices de rotation deviennent des matrices "unités" (car (sin(0)=0 et cos(0)=1), et l'on peut dès lors simplifier le produit en "translation"

|x'|   |x|   |ox|
|y'| = |y| - |oy|
|z'|   |z|   |oz|

Mais il ne faut pas oublié que l'observateur est le 0,0,0; ce qui veut dire que ox=oy=oz=0, alors 

|x'|   |x|
|y'| = |y|
|z'|   |z|

Et l'on retombe bien sur une matrice toute simple.

Conclusions

Il est impossible de conclure simplement, mais je vais tenter de résumer au mieux tout ce qui a été vu, et de fournir une matrice/formule simple

vec quelques définitions voici les formules que nous allons implémenter.

soit:

ox,oy,oz    : coordonnées cartésienne, position de l'oeil dans l'espace.
x[],y[],z[] : coordonnées cartésienne des points a transformer
ex,ey,ez    : coordonnées cartésienne de l'écran
                   avec ex=-(sizex/2), ey=-(sizey/2) et ez=1
ax,ay,az    : angles de vue , ces angles définissent la direction du regard à partir du point de l'observateur (ox,oy,oz).
                   cx = cos(ax), sx = sin(ax), cy = cos(ay), ...
nx,ny,,nz   : coordonnées cartésienne, position temporaire de calcul

pseudo code:

foreach 
{
  dx = x[]-ox
  dy = y[]-oy
  dz = z[]-oz

  nx = cy*(sz*dy + cz*dx) - sy*dz
  ny = sx*(cy*dz + sy*(sz*dy + cz*dx)) + cx*(cz*dy - sz*dx)
  nz = cx*(cy*dz + sy*(sz*dy + cz*dx)) - sx*(cz*dy - sz*dx)

  x' = nx*(ez/nz) + ex
  y' = ny*(ez/nz) + ey
}

Références

3D et vrai Relief 
J.J.Meyer 
ISBN: 2-7091-0990-5
https://www.amazon.co.uk/3D-vrai-relief-images-synthese/dp/2709109905

PC Interdit 2eme édition Windows 95 & Jeux 3D 
Boris Bertelsons, Mathias Rasch et Jan Erik Hoffmann
ISBN: 2-7429-0500-6
https://www.amazon.fr/PC-interdit-Collectif/dp/2742905006

Mathematics of 3D Graphics
Juan David Gonzalez Cobas
Universidad de Oviedo
Blender Conference 2004
https://www.cs.trinity.edu/~jhowland/class.files.cs357.html/blender/blender-stuff/m3d.pdf

http://www.ecere.com/3dbhole/mathematics_of_3d_graphics.html

https://en.wikipedia.org/wiki/3D_projection

https://fr.wikipedia.org/wiki/Projection_(g%C3%A9om%C3%A9trie)

https://fr.wikipedia.org/wiki/Perspective_axonom%C3%A9trique