Pour comprendre l'exemple qui va accompagné cette présentation, je vous conseille vivement de revoir vos mathématiques, notament en ce qui concerne la manipulation de matrices.
La base de la programmation en 3-Dimensions (x, y et z) est consituée des élements suivants:
- Définition du point (x,y,z)
- Transformation de point dans l'espace (translation, rotation, dillatation)
- Définition d'un objet 3D constitué d'une collection de points
Nous n'aborderons pas dans cet article les fonctionnalités avancées de la 3D actuelle telles que l'éclairage et les ombres, mais sachez que tout est faisable, le tout est de savoir gérer les limitations de GDI+ dans ce domaine.
GDI+ en effet, n'a pas été conçu pour effectuer des rendus 3D fluides.
Commençons par la structure la plus simple, le point:
Il se représente dans un repère à 3 axes: x, y et z respectivement l'abscisse, l'ordonnée et la profondeur.

Ces deux représentation sont correctes, et il n'y a pas de règle qui impose le choix de l'un ou de l'autre pour effectuer le rendu de vos objets dans une scène 3D.
En .NET malheureusement pour l'instant il n'existe pas de structure permettant de représenter cette entité. Point et PointF représentent tous deux des points dans un repère x,y donc 2D à la nuance près que PointF traite avec des float plutôt que des int pour Point.
Voici l'implémentation d'un point 3D que je vous propose:
public class Point3D
{
private double m_x;
private double m_y;
private double m_z;
public Point3D()
{
m_x = 0.0;
m_y = 0.0;
m_z = 0.0;
}
public Point3D(double x, double y, double z)
{
m_x = x;
m_y = y;
m_z = z;
}
public PointF Point2D
{
get { return new PointF((float)m_x, (float)m_y); }
}
}
Jusque là pas de soucis, maintenant venons aux éléments les plus interessant utilisés pour faire des animations en 3D: les transformations.
La première transformation que nous allons voir est la translation, cette transformation revient à déplacer le point selon une ligne droite dans son repère.
Par exemple, pour un déplacement horizontal:
Point3D pt = new Point3D();
pt.X += 10;
On peut également effectuer une translation sur les axes y et z pour créer une translation globale déplaçant le point sur les axes x,y et z simultanément.
Nous verrons plus loin lorsque nous aurons étudier les matrices de transformation que la translation peut être effectuer d'une autre manière en appliquant une matrice de transformation au point.
La rotation est l'opération de transformation la plus complexe, et nous allons donc commencer à étudier les matrices permettant d'effectuer les rotations x, y et z.
Les matrices de transformation que je vais utiliser sont de taille 4x4 afin de modéliser un point représenté par (x,y,z,w) ou w est le facteur d'échelle, c'est à dire que pour passer de cette représentation à (X,Y,Z) on effectue (x/w, y/w, z/w).
La matrice permettant d'effectuer la rotation d'un point autour de l'axe x est la suivante:

En C#, on peut la représenter comme suit:
public
struct Matrix3D
{
public double A11;
public double A12;
public double A13;
public double A14;
public double A21;
public double A22;
public double A23;
public double A24;
public double A31;
public double A32;
public double A33;
public double A34;
public double A41;
public double A42;
public double A43;
public double A44;
public static Matrix3D GetRotateX(double teta)
{
Matrix3D m = new Matrix3D();
m.A22 = Math.Cos(teta);
m.A23 = -Math.Sin(teta);
m.A32 = Math.Sin(teta);
m.A33 = Math.Cos(teta);
m.A11 = 1.0;
m.A44 = 1.0;
return m;
}
}
Sur ce modèle on peut représenter également les matrices de zoom et de translation qui sont les suivantes:


Ainsi nous pouvons rajouter au code de notre classe Point3D les méthodes suivantes:
public void ApplyMatrix(Matrix3D m)
{
double w = 0.0;
Point3D pt = new Point3D();
w = m.A41 * m_x + m.A42 * m_y + m.A43 * m_z + m.A44;
pt.m_x = m.A11 * m_x + m.A12 * m_y + m.A13 * m_z + m.A14;
pt.m_y = m.A21 * m_x + m.A22 * m_y + m.A23 * m_z + m.A24;
pt.m_z = m.A31 * m_x + m.A32 * m_y + m.A33 * m_z + m.A34;
if (w != 0.0)
{
pt.m_x /= w;
pt.m_y /= w;
pt.m_z /= w;
}
m_x = pt.m_x;
m_y = pt.m_y;
m_z = pt.m_z;
}
public void Rotate(double tetaX, double tetaY, double tetaZ)
{
ApplyMatrix(Matrix3D.GetRotateX(tetaX));
ApplyMatrix(Matrix3D.GetRotateY(tetaY));
ApplyMatrix(Matrix3D.GetRotateZ(tetaZ));
}
public void Offset(double dx, double dy, double dz)
{
ApplyMatrix(Matrix3D.GetTranslation(dx, dy, dz));
}
public void Zoom(double factor)
{
ApplyMatrix(Matrix3D.GetZoom(factor));
}
Il ne nous reste plus qu'à définir l'objet 3D par la classe C# suivante:
public
class Object3D
{
private string m_name;
private Point3DCollection m_points;
public string Name
{
get { return m_name; }
set { m_name = value; }
}
public Point3DCollection Points
{
get { return m_points; }
}
public Object3D(): this(null) {}
public Object3D(string name)
{
m_points = new Point3DCollection();
m_name = name;
}
public void Rotate(double tetaX, double tetaY, double tetaZ)
{
foreach (Point3D pt in m_points)
{
pt.Rotate(tetaX, tetaY, tetaZ);
}
}
public void Offset(double dx, double dy, double dz)
{
foreach (Point3D pt in m_points)
{
pt.Offset(dx, dy, dz);
}
}
public void Zoom(double factor)
{
foreach (Point3D pt in m_points)
{
pt.Zoom(factor);
}
}
}
Comme vous l'aurez compris, effectuer une transformation sur un objet 3D revient à effectuer la dite transformation sur l'ensemble des points qui le compose.
Pour ceux qui seraient interessé par une librairie contenant toutes les belles choses que nous venons de voir et plus encore, je vous invite à télécharger la solution en cours de développement qui se trouve ici.
