Ce programme construit et affiche une surface 3D statique de type peaks, rendue en projection isométrique, avec facettes pleines, tri de profondeur et trois grilles fixes formant un repère spatial. Voici une analyse complète de son fonctionnement.
Paramétrage de la scène
La toile est définie avec une largeur de 560 px et une hauteur de 520 px. Le centre visuel est légèrement abaissé pour compenser la hauteur du relief :
- OX = LARG / 2
- OY = HAUT / 2 + 40
L’angle de vue est une projection iso inclinée, définie par :
Deux échelles sont utilisées :
- sXY = 62 pour les axes X et Y
- sZ = 13 pour la hauteur
La résolution de la grille est fixée à N = 22, avec un domaine allant de xmin = -3 à xmax = 3.
Calcul du relief : la fonction « peaks »
Le programme pré-calcule les hauteurs z pour chaque point de la grille. Pour chaque couple (i, j), il calcule :
- xx et yy : coordonnées normalisées
Trois composantes du relief :
- La hauteur finale est : z = g1 + g2 + g3
Toutes les valeurs sont stockées dans un tableau linéaire.
Colormap « jet »
Une fonction dédiée convertit une hauteur normalisée en couleur RGB. Elle reproduit le célèbre colormap jet :
- Bleu ? Vert ? Jaune ? Rouge selon la valeur de hn (0 à 1).
Cette palette renforce la lisibilité du relief.
Construction du repère 3D
Avant de dessiner la surface, le programme trace trois grilles fixes :
- Plan XY (sol)
- Plan XZ (mur arrière gauche)
- Plan YZ (mur arrière droit)
Chaque grille est dessinée en lignes fines (#dddddd), ce qui crée un repère spatial clair et stable.
Projection 3D ? 2D avec rotation
La procédure dessinerVue(rot) applique une rotation horizontale :
- cr = cos(rot)
- sr = sin(rot)
Chaque point (x, y, z) est transformé en coordonnées tournées (xr, yr) puis projeté en 2D via l’angle iso :
- pX = OX + (xr·sXY – yr·sXY)·cosA pY = OY – z·sZ – (xr·sXY + yr·sXY)·sinA
Cette projection donne l’effet 3D sans utiliser de moteur graphique.
Tri de profondeur : rendu correct des facettes
Pour éviter que les facettes s’affichent dans le mauvais ordre, le programme utilise un tri par bandes de profondeur :
Chaque facette reçoit une valeur de profondeur.
Le programme parcourt les bandes de la plus éloignée à la plus proche.
Il dessine uniquement les facettes appartenant à la bande courante.
Ce tri garantit un rendu propre, sans chevauchements incohérents.
Dessin des facettes
Chaque cellule de la grille est rendue comme un polygone à 4 sommets :
- Les quatre hauteurs za, zb, zc, zd sont récupérées.
- Les quatre coins sont projetés en 2D.
- La couleur est déterminée par la hauteur moyenne.
- Le polygone est dessiné avec un contour sombre (#0a0a0f).
Le résultat est une surface 3D colorée, continue et lisible.
Résultat final
La toile affiche :
- Le repère 3D
- La surface « peaks » entièrement colorée
- Un titre discret : Surface 3D « peaks »
L’appel final : dessinerVue(0) produit une vue alignée, sans rotation.
Conclusion
Ce programme combine plusieurs techniques graphiques :
- Calcul de relief paramétrique
- Projection isométrique
- Colormap dynamique
- Tri de profondeur manuel
- Rendu polygonal optimisé
Il démontre comment produire une surface 3D complète sans moteur 3D, uniquement via calculs géométriques et dessin 2D. Une base solide pour des visualisations scientifiques, des rendus mathématiques ou des démonstrations pédagogiques.
Programme :
// ============================================================
// SURFACE 3D "peaks" — centrée, statique (sans rotation)
// Facettes pleines, colormap jet, fond noir, tri de profondeur.
// INCLUS : 3 Grilles fixes (XY, XZ, YZ) formant le repère.
// ============================================================
LARG est un nombre
LARG vaut 560
HAUT est un nombre
HAUT vaut 520
// ---- Centre de la toile (surface centrée) ----
OX est un nombre
OY est un nombre
OX vaut LARG / 2
OY vaut HAUT / 2 + 40 // légèrement bas pour compenser la hauteur du relief
// ---- Angle de vue iso (inclinaison verticale) ----
sinA est un nombre
cosA est un nombre
sinA vaut 0,34
cosA vaut 0,62
// ---- Échelles ----
sXY est un nombre
sZ est un nombre
sXY vaut 62
sZ vaut 13
// ---- Résolution ----
N est un nombre
N vaut 22
xmin est un nombre
xmax est un nombre
xmin vaut - 3
xmax vaut 3
// ---- Pré-calcul des hauteurs z : index(i,j) = j*(N+1) + i ----
hauteurs est un tableau
j est un nombre
pour j de 0 à N
i est un nombre
pour i de 0 à N
xx est un nombre
yy est un nombre
xx vaut xmin + (xmax - xmin) * i / N
yy vaut xmin + (xmax - xmin) * j / N
g1 est un nombre
g2 est un nombre
g3 est un nombre
g1 vaut 3 * (1 - xx) * (1 - xx) * puissance(2.71828, -(xx * xx) - (yy + 1) * (yy + 1))
g2 vaut - 10 * (xx / 5 - puissance(xx, 3) - puissance(yy, 5)) * puissance(2.71828, -(xx * xx) - (yy * yy))
g3 vaut - 1 / 3 * puissance(2.71828, -((xx + 1) * (xx + 1)) - (yy * yy))
hauteurs ajoute g1 + g2 + g3
fin pour
fin pour
// ---- Couleur "jet" selon hauteur normalisée hn (0..1) ----
fonction couleur_jet(hn)
hnc est un nombre
hnc vaut limiter(hn, 0, 1)
rC est un nombre
gC est un nombre
bC est un nombre
rC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 3), 0, 1))
gC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 2), 0, 1))
bC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 1), 0, 1))
retourne rgb(rC, gC, bC)
fin fonction
// ============================================================
// DESSINE L'IMAGE pour un angle de rotation "rot" (radians)
// ============================================================
procédure dessinerVue(rot)
Fig est une toile
dimension(Fig, LARG, HAUT)
remplir(Fig, #ffffff)
cr est un nombre
sr est un nombre
cr vaut cosinus(rot)
sr vaut sinus(rot)
// --- DESSIN DES 3 GRILLES FIXES (XY, XZ, YZ) ---
zBase est un nombre
zBase vaut - 6
zMax est un nombre
zMax vaut 8
k est un nombre
pour k de 0 à 10
yL est un nombre
yL vaut xmin + (xmax - xmin) * k / 10
xL est un nombre
xL vaut xmin + (xmax - xmin) * k / 10
zL est un nombre
zL vaut zBase + (zMax - zBase) * k / 10
// 1. GRILLE SOL (Plan XY à Z = zBase)
px1, py1, px2, py2 est un nombre
px1 vaut OX + (xmin * sXY - yL * sXY) * cosA
py1 vaut OY - zBase * sZ - (xmin * sXY + yL * sXY) * sinA
px2 vaut OX + (xmax * sXY - yL * sXY) * cosA
py2 vaut OY - zBase * sZ - (xmax * sXY + yL * sXY) * sinA
ligne(Fig, px1, py1, px2, py2, #dddddd, 1)
px3, py3, px4, py4 est un nombre
px3 vaut OX + (xL * sXY - xmin * sXY) * cosA
py3 vaut OY - zBase * sZ - (xL * sXY + xmin * sXY) * sinA
px4 vaut OX + (xL * sXY - xmax * sXY) * cosA
py4 vaut OY - zBase * sZ - (xL * sXY + xmax * sXY) * sinA
ligne(Fig, px3, py3, px4, py4, #dddddd, 1)
// 2. GRILLE MUR XZ (Arrière-gauche, à Y = xmax)
px5, py5, px6, py6 est un nombre
px5 vaut OX + (xL * sXY - xmax * sXY) * cosA
py5 vaut OY - zBase * sZ - (xL * sXY + xmax * sXY) * sinA
px6 vaut OX + (xL * sXY - xmax * sXY) * cosA
py6 vaut OY - zMax * sZ - (xL * sXY + xmax * sXY) * sinA
ligne(Fig, px5, py5, px6, py6, #dddddd, 1)
px7, py7, px8, py8 est un nombre
px7 vaut OX + (xmin * sXY - xmax * sXY) * cosA
py7 vaut OY - zL * sZ - (xmin * sXY + xmax * sXY) * sinA
px8 vaut OX + (xmax * sXY - xmax * sXY) * cosA
py8 vaut OY - zL * sZ - (xmax * sXY + xmax * sXY) * sinA
ligne(Fig, px7, py7, px8, py8, #dddddd, 1)
// 3. GRILLE MUR YZ (Arrière-droit, à X = xmax)
px9, py9, px10, py10 est un nombre
px9 vaut OX + (xmax * sXY - yL * sXY) * cosA
py9 vaut OY - zBase * sZ - (xmax * sXY + yL * sXY) * sinA
px10 vaut OX + (xmax * sXY - yL * sXY) * cosA
py10 vaut OY - zMax * sZ - (xmax * sXY + yL * sXY) * sinA
ligne(Fig, px9, py9, px10, py10, #dddddd, 1)
px11, py11, px12, py12 est un nombre
px11 vaut OX + (xmax * sXY - xmin * sXY) * cosA
py11 vaut OY - zL * sZ - (xmax * sXY + xmin * sXY) * sinA
px12 vaut OX + (xmax * sXY - xmax * sXY) * cosA
py12 vaut OY - zL * sZ - (xmax * sXY + xmax * sXY) * sinA
ligne(Fig, px11, py11, px12, py12, #dddddd, 1)
fin pour
// --- Passe unique avec tri par profondeur (bandes) pour la surface ---
bande est un nombre
pour bande de 40 à 0 pas - 1
jj est un nombre
pour jj de 0 à N - 1
ii est un nombre
pour ii de 0 à N - 1
// centre (monde) de la facette
xc est un nombre
yc est un nombre
xc vaut xmin + (xmax - xmin) * (ii + 0.5) / N
yc vaut xmin + (xmax - xmin) * (jj + 0.5) / N
// coordonnées tournées pour la surface
xrc est un nombre
yrc est un nombre
xrc vaut xc * cr - yc * sr
yrc vaut xc * sr + yc * cr
// profondeur -> bande entière (grand = au fond)
prof est un nombre
prof vaut arrondi((xrc + yrc + 6) * 40 / 12)
si prof = bande alors
// Les 4 coins (monde), tournés puis projetés
za est un nombre
zb est un nombre
zc2 est un nombre
zd est un nombre
za vaut hauteurs[jj * (N + 1) + ii]
zb vaut hauteurs[jj * (N + 1) + ii + 1]
zc2 vaut hauteurs[(jj + 1) * (N + 1) + ii + 1]
zd vaut hauteurs[(jj + 1) * (N + 1) + ii]
xa est un nombre
ya est un nombre
xb est un nombre
yb est un nombre
xa vaut xmin + (xmax - xmin) * ii / N
ya vaut xmin + (xmax - xmin) * jj / N
xb vaut xmin + (xmax - xmin) * (ii + 1) / N
yb vaut xmin + (xmax - xmin) * (jj + 1) / N
// projette un coin (x,y,z) avec rotation
xr1 est un nombre
yr1 est un nombre
xr1 vaut xa * cr - ya * sr
yr1 vaut xa * sr + ya * cr
pax est un nombre
pay est un nombre
pax vaut OX + (xr1 * sXY - yr1 * sXY) * cosA
pay vaut OY - za * sZ - (xr1 * sXY + yr1 * sXY) * sinA
xr2 est un nombre
yr2 est un nombre
xr2 vaut xb * cr - ya * sr
yr2 vaut xb * sr + ya * cr
pbx est un nombre
pby est un nombre
pbx vaut OX + (xr2 * sXY - yr2 * sXY) * cosA
pby vaut OY - zb * sZ - (xr2 * sXY + yr2 * sXY) * sinA
xr3 est un nombre
yr3 est un nombre
xr3 vaut xb * cr - yb * sr
yr3 vaut xb * sr + yb * cr
pcx est un nombre
pcy est un nombre
pcx vaut OX + (xr3 * sXY - yr3 * sXY) * cosA
pcy vaut OY - zc2 * sZ - (xr3 * sXY + yr3 * sXY) * sinA
xr4 est un nombre
yr4 est un nombre
xr4 vaut xa * cr - yb * sr
yr4 vaut xa * sr + yb * cr
pdx est un nombre
pdy est un nombre
pdx vaut OX + (xr4 * sXY - yr4 * sXY) * cosA
pdy vaut OY - zd * sZ - (xr4 * sXY + yr4 * sXY) * sinA
// Couleur selon hauteur moyenne
zmoy est un nombre
zmoy vaut (za + zb + zc2 + zd) / 4
hn est un nombre
hn vaut (zmoy + 7) / 14
coul est un texte
coul vaut couleur_jet(hn)
contour(Fig, #0a0a0f, 1)
polygone(Fig, pax, pay, pbx, pby, pcx, pcy, pdx, pdy, coul)
contour(Fig, #0)
fin si
fin pour
fin pour
fin pour
label(Fig, 20, 34, "Surface 3d « peaks »", #1F2937, 18)
// Affichage direct de la toile finale
affiche Fig
fin procédure
// ============================================================
// APPEL : on dessine avec un angle de 0 (aligné)
// ============================================================
appelle dessinerVue(0)