3d Chapitre 1 - Représentation 3D à 2D
CHAPITRE 1 - Représentation 3D à 2D
En théorie, vous ne devriez pas être très surpris d'apprendre que votre écran d'ordinateur est seulement capable de représenter des coordonnées en 2 dimensions. Cet état de fait pose un sérieux problème quand on désire modéliser un objet 3D sur l’écran. Comme les écrans de sortie tridimensionnelles n’existe pas encore, nous allons devoir transformer les coordonnées 3D en coordonnées 2D. Pour se faire nous allons utiliser la projection par perspective, qui permet de représenter correctement la « profondeur » d’un objet.
OBJETS 3D
Un objet 3D est décrit par un ensemble de coordonnées (X,Y,Z). Un objet est constitué de plusieurs faces, et ces faces sont naturellement définies par un ensemble de sommet, généralement 3, car les triangles permettent de dessiner toutes les faces possibles. Les coordonnées des sommets possèdent différents point de références dans l’espace. En graphiques 3D, on utilise 4 différent type d’espaces :
- Coordonnées locale (3D) Ce système de coordonnées nous retourne les sommets d’un objet avec comme origine le centre de l’objet. Aussi connu sous le nom de coordonnées d’objet.
- Coordonnées de monde (3D) Ce système de coordonnées nous retourne la position d’un objet dans un monde, avec comme origine le centre de ce monde. C’est la position transformée des objets dans le monde, et c’est avec ces coordonnées qu’on effectue la détection de collisions et l’illumination.
- Coordonnées de caméra (3D) Ce système est relatif à la caméra. Les objets sont transformés des coordonnées de monde vers les coordonnées de caméra pour définir ce qui est visible sur l’écran.
- Coordonnées d’écran (2D) Ce système de coordonnées représente les coordonnées d’écran transformés des coordonnées de caméra vers notre écran 2D. L’origine est le centre de l’écran, et on trouve ces coordonnées par la projection par perspective.
Dans ce chapitre, nous allons assumés 2 choses : premièrement, la caméra est fixe et elle regarde vers le « fond » de l’écran, c’est à dire (0,0,-1). Deuxièmement, nous utilisons un système de coordonnées « main gauche », c’est à dire que X positif va vers la droite, Y positive va vers le haut et Z positif va vers le fond de l’écran :
Comme nous l’avons mentionné plus haut, un objet est constitué d’un ensemble de faces (polygones) et de sommets (vecteurs). En général, nous utilisons le triangle pour dessiner une face, mais dans le cas de l’élément géométrique le plus simple qui soit, le cube, ont peut utiliser un rectangle. Regardons attentivement l’exemple ci-dessous pour illustrer ce concept :
0 -1 -1 -1
1 1 -1 -1
2 1 1 -1
3 -1 1 -1
4 1 -1 1
5 -1 -1 1
6 -1 1 1
7 1 1 1
Faces
0,1,2,3
1,4,7,2
4,5,6,7
5,0,3,6
5,4,1,0
3,2,7,6
En premier lieu, nous définissons les 8 sommets d’un cube. Ensuite, nous assignons aux 6 faces quadrilatères le composant un ensemble de sommets. Par exemple, la face 0 est composé des sommets 0, 1, 2 et 3. Ainsi de suite pour les autres faces :
Après ce brève introduction aux notions fondamentales du 3D, abordons maintenant le problème suivant : nous voulons crée un champ d’étoiles défilantes, représentées par des pixels. Comment allons nous transformer les coordonnées de monde des étoiles vers l’écran?
PROJECTION PERSPECTIVE
Pour représenter un point dans un espace 3D, on se sert du système de coordonnée Cartésien, en y ajoutant la profondeur Z. Il faut donc partir du principe de la perspective pour projeter ces points de façon adéquate. La façon la plus simple d'aborder le problème est de transformer les coordonnées des étoiles XYZ en coordonnées 2D. Donc, nous allons avoir besoin de 2 structures:
- Écran: cette structure contient les coordonnées 2D, relatives à l'origine de l'écran
- Étoiles: elle contient les coordonnées 3D, relatives à l'origine du monde 3D
Afin de transformer les coordonnées 3D en 2D, il faut tenir compte de la loi de la perspective, redécouverte à la Renaissance. Simplement dit, un objet plus loin nous apparaît plus petit (dans notre cas, un point plu foncé est plus loin). La projection se base directement sur cette loi. Donc, voici ce théorème:
ecran.x = x / z
ecran.y = y / z
C'est aussi simple que cela... enfin presque! Le hic, c'est que cette équation assume que nous somme à l'origine. Donc, pour rétablir la situation, nous ajoutons les valeurs correctes. Par exemple, en mode 13h 320x200:
ecran.x = 160 + x / z
ecran.y = 100 + y / z
Finalement, nous devons considérer la distance de la caméra. Cette équation assume que la caméra est collé sur le moniteur. Il n'existe pas de valeur arbitraire parfaite. Souvent, il faut expérimenter, mais dans mon cas une distance de 200 était parfaite.
ecran.x = 160 + DISTANCE * x / z
ecran.y = 100 + DISTANCE * y / z
Pour ce que nous voulons réaliser, il est nécessaire de créer un effet de profondeur aux étoiles qui se déplace sur l'axe Z. Comme je l'avais fait auparavant, il faut ramener la couleur du pixel entre 17-31, tons de gris sur la palette par défaut, dépendant de son Z. L'algorithme, dans l'ordre, devrait ressembler à ceci:
// Pseudocode pour un starfield 3D//
BoucleDébut
Initialiser chacune des étoiles
Pour chaque étoiles
Augmenter son Z (elle se rapproche)
Si elle ne dépasse pas la distance minimum possible
Mettre à jour sa position dans le monde 3D
Pour chaque étoiles
Projection des coordonnées 3D en 2D (monde vers écran)
Calculer la couleur en fonction du Z
Afficher le pixel sur l'écran
BoucleFin
C'est aussi simple que cela! Rien de très compliqué pour commencer, je l'avoue, mais il faut bien commencer quelque part, et ça va se compliquer très rapidement, faites-moi confiance. Bonne chance dans votre apprentissage de la 3D!
En résumé :
Projection 3D vers 2D (par perspective)
Différents espaces 3D : Local, Écran, Monde, Caméra
Représentation d’objet à partir de sommets et faces
3dchap1.cpp
//////////////////////////////////////////////////////////////////////////
// Code source : Shaun Dore //
// Fichier : 3DCHAP1.CPP //
// Date : 19-09-1998 //
// Compilateur : Borland C++ 3.0 16-bit Real Mode //
// Description : Representation de points 3D sur un ecran 2D //
//////////////////////////////////////////////////////////////////////////
// --------------------------- INCLUDE --------------------------------//
#include <mem.h>
#include <conio.h>
#include <stdlib.h>
// ---------------------- CONSTANTES & MACROS --------------------------//
#define DISTANCE 100 // Distance de l'observateur
#define MAX_ETOILES 1000 // Nombre d'etoiles
#define ZMIN -100 // Valeur minimum de Z
#define ZMAX -10 // Valeur maximum de Z
#define DX 160 // Distance X
#define DY 100 // Distance Y
#define DZ (ZMAX-ZMIN) // Distance Z
// ------------------- STRUCTURES DE DONNEES --------------------------//
// Structure pour representer un point dans un espace 2D
typedef struct _2D
{
int x,y;
};
// Structure pour representer un point dans un espace 3D
typedef struct _3D
{
int x,y,z;
};
//------------------------- VARIABLES GLOBALES --------------------------//
char *ecran = (char *) (0xA0000000L); // Pointeur sur memoire video
char *virtuel = new char[64000L]; // Pointeur sur ecran virtuel
_3D Etoiles [MAX_ETOILES]; // Coordonnees 3D des etoiles
_2D Ecran [MAX_ETOILES]; // Coordonnees d'ecran 2D
//---------------------------- FONCTIONS --------------------------------//
void putpixel (int x, int y, unsigned char col)
{
if ( (x>0) && (x<320) && (y>0) && (y<200) )
virtuel[(y << 8) + (y << 6)+x] = col;
}
// Initialise une etoile avec des variables aleatoires
void PlaceEtoile(_3D *Vecteur, int z)
{
Vecteur->x = random(DX)-DX/2;
Vecteur->y = random(DY)-DY/2;
Vecteur->z = z;
}
// Transforme les coordonnees 3D d'un point en coordonnees d'ecran
// par perspective a point de fuite X' = (X/Z), en tenant compte de
// la distance focale et avec translation vers le millieu de l'ecran
void Projection(_3D Vecteur,_2D *Ecran)
{
Ecran->x = 160 + DISTANCE * Vecteur.x / Vecteur.z;
Ecran->y = 100 + DISTANCE * Vecteur.y / Vecteur.z;
}
void main()
{
int couleur;
asm{MOV AX,0x13; INT 0x10} // Mode graphique
randomize(); // Initialise nombre aleatoire
// Initialise les etoiles
for(int e=0; e<MAX_ETOILES; e++) PlaceEtoile(&Etoiles[e],(random(DZ)+ZMIN));
do
{
for(e=0; e<MAX_ETOILES; e++)
{
Etoiles[e].z++; // Approche les etoiles
if(Etoiles[e].z>ZMAX) PlaceEtoile(&Etoiles[e],ZMIN);
}
for(e=0; e<MAX_ETOILES; e++)
{
Projection(Etoiles[e],&Ecran[e]); // Projection
putpixel(Ecran[e].x,Ecran[e].y,(Etoiles[e].z-ZMIN)*15/DZ+16 );
}
while(!(inp(0x3DA)&8)); // retrace verticale
memcpy(ecran,virtuel,64000L); // copie le contenu sur l'ecran physique
memset(virtuel,0,64000L); // efface l'ecran virtuel
} while (!kbhit()); // tant que !clavier
asm{MOV AX,0x03; INT 0x10} // Mode texte
}