Académique Documents
Professionnel Documents
Culture Documents
La
Programmation
Graphique 3D
de WPF 4
Avec l’environnement
de développement
.NET Framework 4 et
Visual Studio 2010
de MICROSOFT
La
programmation
graphique 3D
de
WPF 4
Patrice REY
Toutes les marques citées ont été déposées par leurs propriétaires respectifs.
La loi du 11 Mars 1957 n’autorisant aux termes des alinéas 2 et 3 de l’article 41,
d’une part, que les «copies ou reproductions strictement réservées à l’usage privé
du copiste et non destinées à une utilisation collective», et, d’autre part, que les
analyses et les courtes citations dans un but d’exemple et d’illustration, «toute
représentation ou reproduction intégrale, ou partielle, faite sans le consentement
de l’auteur ou de ses ayants droit ou ayant cause, est illicite» (alinéa 1er de l’article
40).
Cette représentation ou reproduction, par quelque procédé que ce soit,
constituerait donc une contre-façon sanctionnée par les articles 425 et suivants du
Code Pénal.
Éditeur
Books on Demand GmbH,
12/14 rond point des Champs Élysées,
75008 Paris, France
Impression :
Books on Demand GmbH, Norderstedt, Allemagne
ISBN : 978-2-8106-2086-9
Dépôt légal : Juillet 2011
Auteur
Patrice REY
85 rue de vincennes
App 23 B
33000 BORDEAUX
e-mail : reypatrice@orange.fr
Table des matières
Avant-propos .......................................................................................... 7
Chapitre 4 - La scène 3D
1. Visualisation de la scène .................................................................. 95
2. Le contrôle Viewport3D .................................................................... 96
3. La création de modèles 3D ................................................................. 97
3.1 - La classe ModelVisual3D ........................................................... 97
3.2 - La classe UIElement3D .............................................................. 99
3.3 - La classe GeometryModel3D .................................................... 101
4. La géométrie Geometry3D ............................................................... 102
5. Matière et surface .............................................................................. 107
5.1 - La surface mate ......................................................................... 108
5.2 - La surface lumineuse ................................................................ 109
5.3 - Combinaison de matières ......................................................... 109
5.4 - La réflexion spéculaire .............................................................. 110
6. L’éclairage de la scène ........................................................................ 111
7. Les caméras ........................................................................................ 113
Pour réaliser plus facilement des programmes en C#, il est préférable d’utiliser
un environnement de développement intégré ou IDE (Integrated Development
Environment). Cet IDE permet de développer, compiler et exécuter un
programme dans un langage donné qui sera, dans ce livre, le C# (se prononce
C Sharp). Microsoft propose comme IDE soit Visual Studio 2010 (dans sa
version payante avec 30 jours d’essai gratuit), soit Visual C# 2010 Express
(dans sa version allégée mais gratuite).
Puissante interface IDE garantissant la production d’un code de qualité, Visual
Studio 2010 intègre de nouvelles fonctionnalités qui simplifient le processus
de développement d’application, de la conception au déploiement.
Pour télécharger et installer l’environnement de développement intégré,
ouvrez votre navigateur et allez sur les pages web suivantes au choix:
• pour télécharger Visual C# 2010 Express (figure A1): http://msdn.microsoft.
com/fr-fr/express/aa975050.aspx
• pour télécharger une version d’évaluation Visual Studio 2010 Ultimate
avec une période d’essai gratuite de 30 jours (figure A2): http://www.
microsoft.com/france/visualstudio/download
Figure A1
Figure A2
Avant-propos 9
Le contenu du livre
La localisation des points qui représentent une figure 3D dans un espace 3D,
passe par l’utilisation de coordonnées 3D. La structure Point3D représente
un point de coordonnées x, y et z dans l’espace 3D. La structure Vector3D
représente un vecteur dans l’espace 3D.
Z
Les coordonnées (x,y,z) d’un point dans l’espace 3D sont représentées par la
structure Point3D. Cette structure expose les propriétés:
• X qui représente la coordonnée x du Point3D sur l’axe des X.
• Y qui représente la coordonnée y du Point3D sur l’axe des Y.
• Z qui représente la coordonnée z du Point3D sur l’axe des Z.
La classe Point3DCollection est une collection typée d’objets Point3D. La figure
1-2 visualise un point A de coordonnées (3,2,4), de type Point3D.
Y
Figure 1-2
X
A(3,2,4)
Z
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 13
Un vecteur dans l’espace 3D est représenté par une structure Vector3D. Un
vecteur dans l’espace 3D, de type Vector3D, par définition, représente un
déplacement dans l’espace 3D. Cette structure expose les propriétés suivantes:
• X qui représente le composant X de cette structure.
• Y qui représente le composant Y de cette structure.
• Z qui représente le composant Z de cette structure.
• Length qui obtient la longueur de cette structure.
• LengthSquared qui obtient le carré de la longueur de cette structure.
La figure 1-3 montre un vecteur V dont ses composantes sont (4,4,2). Un
vecteur indique un déplacement par ses composantes. Depuis l’origine O, le
déplacement selon X est de 4, le déplacement selon Y est de 4 et le déplacement
selon Z est de 2.
Figure 1-3
Y
V(4,4,2)
4
X
Z
14 La programmation graphique 3D de WPF 4
Figure 1-4
case «x_rbtn_1»: {
Point3D point_1 = new Point3D(2, 1, 3);
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 15
Vector3D vecteur_1 = new Vector3D(7, 3, 1);
Point3D point_resultat = new Point3D();
point_resultat = point_1 + vecteur_1;
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «vecteur_1 : Vector3D(7,3,1)» + RC;
v_resultat += «-> point_resultat = point_1 + vecteur_1» + RC;
v_resultat += «-> point_resultat : Point3D(9,4,4)» + RC;
v_resultat += point_resultat.ToString() + RC;
break;
}
Figure 1-5 Y
B(9,4,4)
1
2 9
X
V(7,3,1)
A(2,1,3)
3
4
case «x_rbtn_3»: {
Point3D point_1 = new Point3D(9,4,4);
Vector3D vecteur_1 = new Vector3D(7, 3, 1);
Point3D point_resultat = new Point3D();
point_resultat = point_1 - vecteur_1;
v_resultat = «point_1 : Point3D(9,4,4)» + RC;
v_resultat += «vecteur_1 : Vector3D(7,3,1)» + RC;
v_resultat += «-> point_resultat = point_1 - vecteur_1» + RC;
v_resultat += «-> point_resultat : Point3D(2,1,3)» + RC;
v_resultat += point_resultat.ToString() + RC;
break;
}
case «x_rbtn_4»: {
Point3D point_1 = new Point3D(9, 4, 4);
Copyright 2011 Patrice REY
4
V(7,3,1)
A(9,4,4)
1
2 9
X
B(2,1,3)
3
4
Figure 1-7
Y
B(9,4,4)
1 AB(7,3,1)
2 9
X
A(2,1,3)
3
4
Copyright 2011 Patrice REY
Z
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 19
v_resultat += «-> vecteur_resultat = point_1 - point_2» + RC;
v_resultat += «-> vecteur_resultat : Point3D(-7,-3,-1)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
case «x_rbtn_6»: {
Point3D point_1 = new Point3D(2, 1, 3);
Point3D point_2 = new Point3D(9, 4, 4);
Vector3D vecteur_resultat = new Vector3D();
vecteur_resultat = Point3D.Subtract(point_2, point_1);
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «point_2 : Point3D(9,4,4)» + RC;
v_resultat += «-> vecteur_resultat =
Point3D.Subtract(point_2, point_1)» + RC;
v_resultat += «-> vecteur_resultat : Point3D(7,3,1)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
vecteur_resultat = Point3D.Subtract(point_1, point_2);
v_resultat += «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «point_2 : Point3D(9,4,4)» + RC;
v_resultat += «-> vecteur_resultat =
Point3D.Subtract(point_1, point_2)» + RC;
v_resultat += «-> vecteur_resultat : Point3D(-7,-3,-1)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
case «x_rbtn_7»: {
Point3D point_1 = new Point3D(2, 1, 3);
point_1.Offset(7, 3, 1);
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «-> point_1.Offset(7, 3, 1)» + RC;
v_resultat += «-> point_1 : Point3D(9,4,4)» + RC;
v_resultat += point_1.ToString() + RC;
break;
}
20 La programmation graphique 3D de WPF 4
Les cases à cocher 08, 09 et 10 permettent de tester l’égalité entre deux points
Point3D. La case 08 utilise l’opérateur surchargé = de la structure Point3D (bool
sont_egaux = (point_1 == point_2);). La case 09 utilise la méthode statique
Point3D.Equals(..), prenant en paramètre deux objets Point3D (bool sont_egaux
= Point3D.Equals(point_1, point_2);). La case 10 utilise la méthode non statique
Equals(..) sur un objet Point3D (bool sont_egaux = point_1.Equals(point_2);).
case «x_rbtn_8»: {
Point3D point_1 = new Point3D(2, 1, 3);
Point3D point_2 = new Point3D(9, 4, 4);
bool sont_egaux = (point_1 == point_2);
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «point_2 : Point3D(9,4,4)» + RC;
v_resultat += «-> sont_egaux = (point_1 == point_2)» + RC;
v_resultat += «-> sont_egaux = false» + RC;
v_resultat += sont_egaux.ToString() + RC;
break;
}
case «x_rbtn_9»: {
Point3D point_1 = new Point3D(2, 1, 3);
Point3D point_2 = new Point3D(9, 4, 4);
bool sont_egaux = Point3D.Equals(point_1, point_2);
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «point_2 : Point3D(9,4,4)» + RC;
v_resultat += «-> sont_egaux =
Point3D.Equals(point_1, point_2)» + RC;
v_resultat += «-> sont_egaux = false» + RC;
v_resultat += sont_egaux.ToString() + RC;
break;
}
case «x_rbtn_10»: {
Point3D point_1 = new Point3D(2, 1, 3);
Point3D point_2 = new Point3D(9, 4, 4);
bool sont_egaux = point_1.Equals(point_2);
v_resultat = «point_1 : Point3D(2,1,3)» + RC;
Copyright 2011 Patrice REY
case «x_rbtn_11»: {
Point3D point_1 = new Point3D();
point_1 = Point3D.Parse(«2,1,3»);
v_resultat = «point_1 = new Point3D()» + RC;
v_resultat += «point_1 = Point3D.Parse(\»2,1,3\»)» + RC;
v_resultat += point_1.ToString() + RC;
break;
}
case «x_rbtn_12»: {
Point3D point_1 = new Point3D(2,1,3);
Point3D point_2 = new Point3D(9, 4, 4);
bool sont_inegaux = (point_1 != point_2);
v_resultat = «point_1 = new Point3D(2,1,3)» + RC;
v_resultat += «point_2 = new Point3D(9, 4, 4)» + RC;
v_resultat += «sont_inegaux = (point_1 != point_2)» + RC;
v_resultat += «-> sont_inegaux = true» + RC;
v_resultat += sont_inegaux.ToString() + RC;
break;
}
case «x_rbtn_13»: {
Vector3D vecteur_1 = new Vector3D(1, 3, 5);
Vector3D vecteur_2 = new Vector3D(3, -3, 0);
Vector3D vecteur_resultat = vecteur_1 + vecteur_2;
v_resultat = «vecteur_1 = new Vector3D(1, 3, 5)» + RC;
v_resultat += «vecteur_2 = new Vector3D(3, -3, 0)» + RC;
v_resultat += «vecteur_resultat = vecteur_1 + vecteur_2» + RC;
22 La programmation graphique 3D de WPF 4
v_resultat += «-> vecteur_resultat : Vector3D(4,0,5)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;}
case «x_rbtn_14»: {
Vector3D vecteur_1 = new Vector3D(1, 3, 5);
Vector3D vecteur_2 = new Vector3D(3, -3, 0);
Vector3D vecteur_resultat = Vector3D.Add(vecteur_1, vecteur_2);
v_resultat = «vecteur_1 = new Vector3D(1, 3, 5)» + RC;
v_resultat += «vecteur_2 = new Vector3D(3, -3, 0)» + RC;
v_resultat += «vecteur_resultat =
Vector3D.Add(vecteur_1, vecteur_2)» + RC;
v_resultat += «-> vecteur_resultat : Vector3D(4,0,5)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;}
Figure 1-8 Y
V2(3,-3,0)
V1(1,3,5)
V(4,0,5)
Copyright 2011 Patrice REY
Z
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 23
Les cases à cocher 15 et 16 permettent d’effectuer la soustraction de deux
structures Vector3D. Le choix de la case 15 permet d’effectuer la soustraction
en utilisant l’opérateur surchargé - de Vector3D. Le choix de la case 16 permet
d’effectuer la soustraction par la méthode statique Vector3D.Substract().
case «x_rbtn_15»: {
Vector3D vecteur_1 = new Vector3D(1, 3, 5);
Vector3D vecteur_2 = new Vector3D(3, -3, 0);
Vector3D vecteur_resultat = vecteur_1 - vecteur_2;
v_resultat = «vecteur_1 = new Vector3D(1, 3, 5)» + RC;
v_resultat += «vecteur_2 = new Vector3D(3, -3, 0)» + RC;
v_resultat += «vecteur_resultat = vecteur_1 - vecteur_2» + RC;
v_resultat += «-> vecteur_resultat : Vector3D(-2,6,5)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
case «x_rbtn_16»: {
Vector3D vecteur_1 = new Vector3D(1, 3, 5);
Vector3D vecteur_2 = new Vector3D(3, -3, 0);
Vector3D vecteur_resultat = Vector3D.Subtract(vecteur_1, vec-
teur_2);
v_resultat = «vecteur_1 = new Vector3D(1, 3, 5)» + RC;
v_resultat += «vecteur_2 = new Vector3D(3, -3, 0)» + RC;
v_resultat += «vecteur_resultat =
Vector3D.Substract(vecteur_1, vecteur_2)» + RC;
v_resultat += «-> vecteur_resultat : Vector3D(-2,6,5)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
case «x_rbtn_17»: {
Vector3D vecteur_1 = new Vector3D(3, 1, -2);
double scalaire = 2;
Vector3D vecteur_resultat = vecteur_1 * scalaire;
24 La programmation graphique 3D de WPF 4
v_resultat = «vecteur_1 = new Vector3D(3,1,-2)» + RC;
v_resultat = «scalaire = 2» + RC;
v_resultat += «vecteur_resultat = vecteur_1 * scalaire» + RC;
v_resultat += «-> vecteur_resultat : Vector3D(6,2,-4)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
case «x_rbtn_18»: {
Vector3D vecteur_1 = new Vector3D(3, 1, -2);
double scalaire = 2;
Vector3D vecteur_resultat = Vector3D.Multiply(scalaire, vecteur_1);
v_resultat = «vecteur_1 = new Vector3D(3,1,-2)» + RC;
v_resultat = «scalaire = 2» + RC;
v_resultat += «vecteur_resultat =
Vector3D.Multiply(scalaire, vecteur_1)» + RC;
Figure 1-9 Y
V2(6,2,-4)
Copyright 2011 Patrice REY
V1(3,1,-2)
Z
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 25
v_resultat += «-> vecteur_resultat : Vector3D(6,2,-4)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
break;
}
Figure 1-10 Y
V(4,4,4)
-> Length = 6.92
-> LengthSquared = 48
V(4,4,4)
Z
26 La programmation graphique 3D de WPF 4
case «x_rbtn_19»: {
Vector3D vecteur_1 = new Vector3D(4, 4, 4);
v_resultat = «vecteur_1 = new Vector3D(4,4,4)» + RC;
v_resultat += «-> vecteur_1.Length = 6.92» + RC;
v_resultat += vecteur_1.Length.ToString() + RC;
break;
}
case «x_rbtn_20»: {
Vector3D vecteur_1 = new Vector3D(4, 4, 4);
v_resultat = «vecteur_1 = new Vector3D(4,4,4)» + RC;
v_resultat += «-> vecteur_1.LengthSquared = 48» + RC;
v_resultat += vecteur_1.LengthSquared.ToString() + RC;
break;
}
case «x_rbtn_21»: {
Vector3D vecteur_1 = new Vector3D(4, 4, 4);
vecteur_1.Normalize();
v_resultat = «vecteur_1 = new Vector3D(4,4,4)» + RC;
v_resultat = «vecteur_1.Normalize()» + RC;
v_resultat = «-> vecteur_1 : Vector3D(0.57,0.57,0.57)» + RC;
v_resultat += «-> vecteur_1.Length = 1» + RC;
v_resultat += vecteur_1.ToString() + RC;
break;
}
case «x_rbtn_22»: {
Vector3D vecteur_1 = new Vector3D(4, 0, 0);
Vector3D vecteur_2 = new Vector3D(0, 0, 4);
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 27
double angle_entre;
angle_entre = Vector3D.AngleBetween(vecteur_1, vecteur_2);
v_resultat = «vecteur_1 = new Vector3D(4,0,0)» + RC;
v_resultat = «vecteur_2 = new Vector3D(0,0,4)» + RC;
v_resultat = «-> angle_entre =
Vector3D.AngleBetween(vecteur_1, vecteur_2)» + RC;
v_resultat += «-> angle_entre = 90» + RC;
v_resultat += angle_entre.ToString() + RC;
break;
}
Figure 1-11
V1(4,0,0)
X
V2(0,0,4)
angle = 90 degrés
Z
28 La programmation graphique 3D de WPF 4
P2(8,1,4)
Tx=4
Ty=1
P1(4,0,6) Tz=-2
Z
La mise à l’échelle d’une figure consiste à l’agrandir ou à la réduire en fonction
des facteurs d’échelle Sx selon l’axe des X, Sy selon l’axe des Y et Sz selon l’axe
des Z. Le point P2(x2,y2,z2) est obtenu en appliquant au point P1(x1,y1,z1)
une matrice de transformation. L’équation correspondant à P2=Ms.P1 est la
suivante:
30 La programmation graphique 3D de WPF 4
Le calcul matriciel donne comme résultat x2=Sx*x1, y2=Sy*y1 et z2=Sz*z1.
Les coordonnées du point P2(x2,y2,z2) sont obtenues par le calcul matriciel
des coordonnées du point P1(x1,y1,z1) par la matrice de mise à l’échelle:
Figure 1-13 Y
P2(8,4,8)
P1(4,2,4)
Sx=2
Sy=2
Sz=2
Z
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 31
La figure 1-13 visualise une mise à l’échelle uniforme de valeur 2 avec
S(Sx=2,Sy=2,Sz=2). Le point P1(4,2,4) donne par cette mise à l’échelle le point
P2(8,4,8).
La réflexion, dit aussi effet miroir, est un cas particulier de la mise à l’échelle. La
figure ci-dessous visualise la matrice pour un effet miroir selon l’axe des Y (on
trouve x2=-x1, y2=y1 et z2=z1).
La figure ci-dessous visualise la matrice pour un effet miroir selon l’axe des Z
(on trouve x2=x1, y2=-y1 et z2=z1).
La figure ci-dessous visualise la matrice pour un effet miroir selon l’axe des X
(on trouve x2=x1, y2=y1 et z2=-z1).
La matrice de rotation pour faire pivoter les points d’un angle α autour de
l’axe des X est la suivante:
B2(0,5,0) D2(6,5,0)
C1(6,0,2)
A1(0,0,2)
B1(0,0,5) D1(6,0,5)
Z
34 La programmation graphique 3D de WPF 4
De la même façon, la matrice de rotation pour faire pivoter les points d’un
angle α autour de l’axe des Y est la suivante:
Et la matrice de rotation pour faire pivoter les points d’un angle α autour de
l’axe des Z est la suivante: Copyright 2011 Patrice REY
La figure 1-15 visualise un cisaillement selon l’axe des X avec Cy=2 et Cz=2
pour une figure représentant un carré. D’après l’équation matricielle, un point
A1(1,2,0) donnera un point A2(5,2,0) avec Cy=2 et Cz=2.
Figure 1-15 Y
B2(0,5,0)
Cy=2
Cz=2
Et on aura la matrice pour un cisaillement selon l’axe des Z qui sera la suivante:
La matrice de rotation utilisée pour une rotation d’un angle α autour de l’axe
des Z donnera x2=x1*cos(α)-y1*sin(α), y2=x1*sin(α)+y1*cos(α) et z2=z1.
Le cisaillement qui permet de déformer les objets pourra se faire selon les
trois axes. D’où trois matrices de transformation concernant le cisaillement.
Suivant l’axe des X, la matrice de cisaillement donnera x2=x1+y1*Cy+z1*Cz,
y2=y1 et z2=z1.
42 La programmation graphique 3D de WPF 4
Dans WPF, la structure Point4D permet de gérer des points de type Point3D
dans le système des coordonnées homogènes. Elle est spécifiquement utilisée
pour accomplir des transformations avec des matrices 3D non affines. Elle
expose les propriétés suivantes:
• X qui définit la coordonnée suivant l’axe des X.
• Y qui définit la coordonnée suivant l’axe des Y.
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 43
• Z qui définit la coordonnée suivant l’axe des Z.
• W qui définit la quatrième coordonnée dans le système des coordonnées
homogènes.
La structure Point4D possède un ensemble de méthodes pour accomplir des
opérations mathématiques variées sur des objets Point4D. Les méthodes
suivantes sont généralement les plus utilisées:
• Point4D.Add qui est une méthode statique qui ajoute une structure Point4D
à un Point4D.
• Point4D.Substract qui est une méthode statique qui soustrait une structure
Point4D à une autre structure Point4D.
• Point4D.Multiply qui est une méthode statique qui transforme la structure
Point4D spécifiée par la structure Matrix3D indiquée.
• Offset qui déplace la structure Point4D de la valeur spécifiée.
• Point4D.Parse qui est une méthode statique qui convertit une représentation
textuelle d’une structure Point4D en une structure Point4D équivalente.
• Point4D.Equals qui est une méthode statique qui compare l’égalité de deux
structures Point4D.
• Equals qui est une méthode qui compare l’égalité de deux structures
Point4D.
La structure Point4D possède des opérateurs surchargés comme:
• + (addition) qui ajoute une structure Point4D à un Point4D.
• - (soustraction) qui soustrait une structure Point4D d’une autre structure
Point4D et retourne le résultat sous forme de Point4D.
• * (multiplication) qui transforme la structure Point4D spécifiée par la structure
Matrix3D indiquée.
• == (égalité) qui compare l’égalité de deux structures Point4D.
• != (inégalité) qui compare l’inégalité de deux structures Point4D.
Il est possible d’effectuer un cast d’un objet Point3D en un objet Point4D comme
dans
Point4D pt4 = (Point4D)new Point3D(10, 15, 20)
Il en est de même pour passer d’un Vector3D à un Point4D comme dans
Point4D pt4 = (Point4D)(Point3D)new Vector3D(10, 15, 20)
A noter que lors de ces cast, la coordonnée homogène w est fixée à 1.
44 La programmation graphique 3D de WPF 4
3 - La structure Matrix3D
La structure Matrix3D est une structure qui représente une matrice 4x4 dans le
système des coordonnées homogènes. Ses composantes sont les suivantes:
en dernière ligne de la matrice. Donc les colonnes d’une matrice dans WPF
correspondent aux lignes d’une matrice dans l’algèbre linéaire classique.
Prenons comme exemple un point P1 aux coordonnées (5,0,0). Si nous
appliquons à P1 une matrice de rotation d’un angle de -90° autour de l’axe
Y, nous obtiendrons un point P2 aux coordonnées (0,0,5). En algèbre linéaire
classique, nous avons donc la relation suivante de positionnement des
coordonnées (le calcul matriciel se fait par ligne):
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 45
Dans la matrice de WPF, les lignes deviennent des colonnes. Le calcul matriciel
se fait donc de la façon suivante:
On retrouve bien les coordonnées de P2 qui sont (0,0,5). Les propriétés M11,
M12, M13, M21, M22, M23, M31, M32 et M33 sont donc réservées pour les
transformations de rotation, mise à l’échelle et inclinaison (ou cisaillement).
Les propriétés OffsetX, OffsetY et OffsetZ sont réservées à la translation.
if (mat1.HasInverse == true) {
x_infos.Text += «matrice mat1 est inversible» + RC;
mat2 = mat1;
mat2.Invert();
...
}
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 47
Figure 1-16
Pour vérifier que la matrice mat2 est bien une matrice inversée de la matrice
mat1, on instancie une nouvelle matrice mat3 et on lui affecte le produit
matriciel de mat1 par mat2.
if (mat1.HasInverse == true) {
...
Matrix3D mat3 = new Matrix3D();
mat3 = mat1 * mat2;
...
}
48 La programmation graphique 3D de WPF 4
Il est à noter que la structure Matrix3D possède une surcharge des opérateurs
qui sont:
• * (multiplication) qui multiplie les matrices spécifiées.
• == (égalité) qui compare l’égalité exacte de deux instances de Matrix3D.
• != (inégalité) qui compare l’inégalité exacte de deux instances de Matrix3D.
Le code procédural nous donne le calcul. Si nous effectuons le calcul à la main
pour vérifier que le produit des matrices donne bien la matrice identité, ce
calcul se fait de la façon suivante: les deux matrices sont des matrices 4x4, le
produit sera une matrice 4x4 et chacune des composantes résultera du calcul
d’une ligne par une colonne. Ci-dessous le détail du calcul des 16 composantes
N1 à N16.
matrice matrice
matrice inversée identité
C1 C2 C3 C4 N1 = L1*C1 = 1*1+0*0+0*0+0*(-4)=1
N2 = L1*C2 = 1*0+0*1+0*0+0*(-2)=0
N3 = L1*C3 = 1*0+0*0+0*1+0*(-1)=0
N4 = L1*C4 = 0
N5 = L2*C1 = 0
N6 = L2*C2 = 1
N7 = L2*C3 = 0
N8 = L2*C4 = 0
N9 = L3*C1 = 0
L1 N10 = L3*C2 = 0
L2 N11 = L3*C3 = 1
N12 = L3*C4 = 0
L3
N13 = L4*C1 = 0
Copyright 2011 Patrice REY
L4 N14 = L4*C2 = 0
N15 = L4*C3 = 0
N16 = L4*C4 = 1
Comme le visualise la figure 1-17, nous avons un triangle repéré par les points
P1(3,0,1), P2(3,0,5) et P3(3,1,5). Nous allons appliquer à ces trois points la
matrice mat1 de façon à définir le triangle après la transformation (repéré par
les points P1t, P2t et P3t).
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 49
Figure 1-17 Y
P1t
X
P1 P3t
P3 P2t
P2
D’un point de vue mathématique, comme nous avons pu le voir, les matrices
3D de WPF sont des matrices transposées par rapport à celles utilisées dans
l’algèbre linéaire classique. Le détail du calcul des positions de P1t en fonction
de P1 et de la matrice mat1 est le suivant:
calcul de x2
calcul de y2
calcul de z2
calcul de w
//matrice originale
Matrix3D mat_orig = new Matrix3D(
1, 2, 3, 4,
2, 1, 0, 0,
Copyright 2011 Patrice REY
0, 0, 1, 0,
1, 2, 3, 1);
AfficherMatriceTexte(mat_orig, «matrice originale mat_orig»);
//mise a l’echelle
Matrix3D mat_scale = mat_orig;
mat_scale.Scale(new Vector3D(0.5, 1.5, 2.5));
AfficherMatriceTexte(mat_scale,
«mat_scale: mat_scale.Scale(new Vector3D(0.5, 1.5, 2.5))»);
//translation
Matrix3D mat_trans = mat_orig;
mat_trans.Translate(new Vector3D(100,150,200));
AfficherMatriceTexte(mat_trans, «mat_trans:
mat_trans.Translate(new Vector3D(100,150,200))»);
AfficherMatriceTexte(mat_trans_prepend, «mat_trans_prepend:
mat_trans.TranslatePrepend(new Vector3D(100,150,200))»);
//rotation
Matrix3D mat_rotate = mat_orig;
mat_rotate.Rotate(new Quaternion(new Vector3D(1, 2, 3), 45));
AfficherMatriceTexte(mat_rotate, 3, «mat_rotate:
mat_rotate.Rotate(new Quaternion(new Vector3D(1, 2, 3), 45))»);
56 La programmation graphique 3D de WPF 4
Pour calculer le quaternion à partir du vecteur représentant l’axe de rotation
et l’angle de rotation, le vecteur représentant l’axe doit être normalisé. Vous
pouvez calculer les composantes du quaternion en une unité d’axe de rotation
et d’angle de rotation.
Supposons que l’axe de rotation pour une rotation 3D soit représenté par un
vecteur unité, de type Vector3D, avec les composantes ax, ay et az, et avec
β l’angle de rotation. Le quaternion aura comme composantes (x,y,z,w) en
coordonnées homogènes données par la formule:
x=ax*sin(β/2) , y=ay*sin(β/2), z=az*sin(β/2) et w=cos(β/2).
Dans notre exemple, nous effectuons une rotation de la matrice mat_orig
d’un angle de 45 degrés autour d’un axe spécifié par Vector3D(1,2,3). La
normalisation du vecteur donne (avec 14=12+22+32)
La méthode RotateAt fait pivoter une structure Matrix3D sur le Point3D spécifié.
Elle reçoit en paramètre une structure Quaternion et un Point3D qui représente
le point central sur lequel pivoter.
CHAPITRE 2
Les projections
A’
B’ plan de projection
les projecteurs
centre de projection
Figure 2-2
A
A’
B’ plan de projection
les projecteurs
centre de projection
à l’infini
Copyright 2011 Patrice REY
Si la distance entre le centre et le plan est finie, la projection est alors une
projection perspective. Si la distance entre le centre et le plan est infinie, la
projection est alors une projection parallèle. Dans une projection perspective,
le centre de projection est explicitement spécifié. Dans une projection parallèle,
la direction de projection est explicitement spécifiée.
CHAPITRE 2 □ Les projections 61
Z
62 La programmation graphique 3D de WPF 4
La figure 2-4 visualise la construction d’une perspective à deux points de
fuite principaux. Les lignes parallèles à l’axe Y ne convergent pas dans la
projection. Ce type de perspective est couramment employé dans les dessins
d’architecture, d’ingénierie et de conception industrielle.
Figure 2-4
Y
point de fuite
point de fuite sur l’axe Z
sur l’axe X
Z X
Y
point de fuite
sur l’axe Z
point de fuite
sur l’axe X
Les projections parallèles peuvent être classées selon deux types: suivant
la relation entre la direction de projection et suivant la normale du plan de
projection. Pour les projections parallèles orthonormales, ces directions sont
soit identiques soit dans le sens inverse, la direction de projection est donc
perpendiculaire au plan de projection. En projections parallèles obliques, ce
n’est pas le cas.
Les projections orthonormales les plus connues sont la projection de façade,
la projection de plan, et la projection de côté. Pour toutes ces projections, le
plan de projection est perpendiculaire à un axe principal qui représente donc
la direction de projection.
La figure 2-6 montre la construction de ces trois types de projections qui sont
souvent utilisées dans les dessins d’ingénierie pour représenter des pièces, des
assemblages et des structures.
Z X
Figure 2-6
plan de
projection de
plan
plan de projection
de côté
plan de
projection de
façade
projection projection
projection projection avec 1 point de avec 3 points
orthographique oblique fuite de fuite
projection de
projection de cabinet projection
plan
perspective avec 2 points
projection de cavalière de fuite
façade
autres
projection de projections
côté
projection projection isométrique
axonométrique
autres projections
Pour effectuer un rendu de la scène 3D, il faut positionner une caméra dans la
scène. Les figures 2-8 et 2-9 visualisent une caméra perspective avec son plan
de projection et son volume d’observation. Tous les points des objets 3D qui
se trouvent dans le volume d’observation, vont être projetés dans le plan de
projection. Le point projeté sera à l’intersection du plan de projection et de la
droite qui passe par le centre de projection et par le point de l’objet 3D.
CHAPITRE 2 □ Les projections 65
Figure 2-8
plan de
projection
haut
Y gauche
Z
bas droit
X
centre de
projection
(caméra) plan de
près volume
d’observation
plan de loin
plan de
projection
hauteur
Y
largeur
Z
angle de
X vue
centre de
projection
(caméra)
volume
d’observation
plan de loin
66 La programmation graphique 3D de WPF 4
Le rendu de la scène 3D est donc une projection géométrique plane 2D. De
plus, la caméra (figure 2-10) est positionnée dans l’espace 3D. Elle vise un
endroit de l’espace par son axe Z (c’est la direction du regard ou Look Direction).
Elle peut aussi pivoter sur elle-même d’un angle dans le plan XY (c’est la
direction du haut ou Up Direction). Avec un plan de projection perpendiculaire
à l’axe Z de la caméra, si la direction du haut tourne, les points projetés sur le
plan de projection seront à des emplacements différents.
Figure 2-10
direction du
haut (up
direction) plan de
projection direction du
regard (look
Y direction)
Z
angle de
vue
centre de
projection
(caméra) X
volume
d’observation
plan de loin
Y
X P(x,y,z)
Pp(xp,yp,zp)
X Y
P(x,y,z) P(x,y,z)
Pp(xp,yp,zp) Pp(xp,yp,zp)
Z Z
d d
70 La programmation graphique 3D de WPF 4
Voyons le cas de la projection d’un point P sur un plan de projection qui est
parallèle au plan XY, et se situe en z=d. Afin de calculer Pp=(xp,yp,zp), c’est-
à-dire la projection perspective de P(x,y,z) sur le plan de projection en z=d,
nous utilisons les triangles similaires représentés dans la figure 2-12 et nous
trouvons les rapports:
Pour effectuer un rendu de la scène 3D, il faut positionner une caméra dans la
scène. Les figures 2-13 et 2-14 visualisent une caméra parallèle avec son plan
de projection et son volume d’observation. Tous les points des objets 3D qui
Copyright 2011 Patrice REY
gauche
Z
X droit
centre de bas
projection à
l’infini (caméra)
plan de volume
près d’observation plan de
loin
De plus, la caméra (figure 2-14) est positionnée dans l’espace 3D. Elle vise
un endroit de l’espace par son axe Z (c’est la direction du regard ou Look
Direction). Elle peut aussi pivoter sur elle-même d’un angle dans le plan
XY (c’est la direction du haut ou Up Direction). Avec un plan de projection
perpendiculaire à l’axe Z de la caméra, si la direction du haut tourne, les points
projetés sur le plan de projection seront à des emplacements différents. Le
plan de projection est caractérisé par sa largeur et sa hauteur, et par la distance
qui le sépare de la caméra.
Figure 2-14 plan de
projection
hauteur
X largeur
centre de
projection à
l’infini (caméra)
plan de volume
près d’observation plan de
loin
74 La programmation graphique 3D de WPF 4
L’UserControl ProjectionParallele.xaml, dans le dossier chapitre02, permet de
mettre en œuvre l’utilisation d’une projection parallele en fixant les différents
paramètres de la caméra pour déterminer le rendu en 2D (figure 2-15).
Différents contrôles de type Slider permettent de régler, dans des plages de
valeurs, la position de la caméra dans l’espace 3D, la direction du regard de
la caméra et l’inclinaison (direction du haut) de la caméra. Le cube possède
six faces de couleurs différentes. Il y a six boutons qui permettent d’effectuer
un réglage prédéfini de façon à voir le cube de face, de droite, de gauche, de
dessus, de dessous et par l’arrière. Un dernier bouton permet de revenir à la
position initiale. Les axes sont repérés par des couleurs: rouge pour l’axe des
X, bleu pour l’axe des Y et vert pour l’axe des Z.
D’un point de vue mathématique (figure 2-16), on a xp=d, yp=d et zp=d.
Figure 2-16
Y
X
Pp(xp,yp,zp) P(x,y,z)
X Y
Pp(xp,yp,zp) P(x,y,z) Pp(xp,yp,zp) P(x,y,z)
Z Z
d d
Les transformations 3D
Les classes de transformations 3D de haut niveau qui sont fournies par WPF
héritent de la classe abstraite Transform3D. La classe Transform3D se trouve dans
l’espace de noms System.Windows.Media.Media3D (assembly PresentationCore.
dll).
Comme le visualise l’arbre d’héritage de cette classe (figure 3-1), ses classes
dérivées sont:
• AffineTransform3D est une classe abstraite qui sert de classe de base
de laquelle dérivent toutes les transformations affines concrètes 3D
(translation, rotation et mise à l’échelle).
• MatrixTransform3D est une classe qui crée une transformation spécifiée par
une structure Matrix3D, utilisée pour manipuler des objets ou des systèmes
de coordonnées dans l’espace universel 3D.
• Transform3DGroup est une classe qui représente une transformation qui
correspond à un élément composite des enfants Transform3D de la collection
Transform3DCollection.
Les classes dérivées de la classe AffineTransform3D représentent les
transformations de haut niveau. Il y a:
• ScaleTransform3D est une transformation affine qui met à l’échelle
(agrandissement ou réduction) un objet dans le plan (XYZ) tridimensionnel,
à partir d’un point central défini; les facteurs d’échelle sont définis sur les
axes X, Y et Z à partir d’un point central.
80 La programmation graphique 3D de WPF 4
• TranslateTransform3D est une transformation affine qui déplace un objet
dans le plan (XYZ) tridimensionnel.
• RotateTransform3D est une transformation affine qui spécifie une
transformation de rotation dans l’espace 3D.
Figure 3-1
2 - La transformation ScaleTransform3D
3 - La transformation TranslateTransform3D
<Transform3DGroup>
<TranslateTransform3D
x:Name='x_cube_trans'
OffsetX='0' OffsetY='0'
OffsetZ='0'>
</TranslateTransform3D>
</Transform3DGroup>
4 - La transformation RotateTransform3D
<Transform3DGroup>
<RotateTransform3D
x:Name='x_cube_rotate'
CenterX='0' CenterY='0'
CenterZ='0'>
</RotateTransform3D>
</Transform3DGroup>
Les glissières font varier l’angle de rotation autour d’un axe dans une plage de
valeurs allant de -180 degrés à +180 degrés. La méthode FaireTournerCube(..)
permet de faire tourner le cube autour d’un axe selon un angle de rotation.
Elle prend comme paramètres un vecteur normalisé pour indiquer l’axe choisi
et une valeur d’angle.
Pour indiquer l’axe X, on choisira un Vector3D(1,0,0). Pour indiquer l’axe
Y, on choisira un Vector3D(0,1,0). Pour indiquer l’axe Z, on choisira un
Vector3D(0,0,1).
88 La programmation graphique 3D de WPF 4
Figure 3-4
Pour exprimer en XAML une rotation d’un angle de 90 degrés autour de l’axe
Y, on écrira:
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D Axis= «0 1 0» Angle= «90»/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
90 La programmation graphique 3D de WPF 4
<QuaternionRotation3D Quaternion= «0 0.707 0 0.707»/>
</RotateTransform3D.Rotation>
</RotateTransform3D>
5 - La transformation MatrixTransform3D
Dès que l’on modifie la position d’une glissière de type Slider, on affiche
la valeur au format texte au-dessus de la glissière et on appelle la méthode
FixerMatriceTransformation().
Le bas de la figure 3-5 montre le cube dans ces trois positionnements avec
l’inclinaison effectuée.
6 - Combinaison de transformations 3D
<Transform3DGroup>
<ScaleTransform3D x:Name=»x_echelle»>
</ ScaleTransform3D>
<RotateTransform3D
x:Name=»x_rotate»
CenterX=»0» CenterY=»0» CenterZ=»0»>
Copyright 2011 Patrice REY
</RotateTransform3D>
</Transform3DGroup>
La scène 3D
1 - Visualisation de la scène
2 - Le contrôle Viewport3D
3 - La création de modèles 3D
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<GeometryModel3D ...></GeometryModel3D>
<GeometryModel3D ...></GeometryModel3D>
<GeometryModel3D ...></GeometryModel3D>
...
</Model3DGroup>
CHAPITRE 4 □ La scène 3D 99
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D>
...
</ModelVisual3D>
<ModelVisual3D>
...
</ModelVisual3D>
...
</ModelVisual3D>
<ModelUIElement3D>
<ModelUIElement3D.Model>
<Model3DGroup>
<GeometryModel3D></GeometryModel3D>
<GeometryModel3D></GeometryModel3D>
<GeometryModel3D></GeometryModel3D>
...
</Model3DGroup>
</ModelUIElement3D.Model>
</ModelUIElement3D>
<ContainerUIElement3D>
<ContainerUIElement3D>
...
</ContainerUIElement3D>
<ContainerUIElement3D>
CHAPITRE 4 □ La scène 3D 101
...
</ContainerUIElement3D>
...
</ContainerUIElement3D>
Figure 4-4
102 La programmation graphique 3D de WPF 4
Un objet GeometryModel3D définit un modèle basé sur une géométrie 3D. Il
sert à réaliser un modèle 3D au moyen de ses propriétés:
• Geometry qui représente une géométrie 3D.
• Material qui représente une matière pour la face avant spécifiée.
• BackMaterial qui représente une matière pour la face arrière spécifiée
(propriété facultative).
4 - La géométrie Geometry3D
La classe Geometry3D est une classe abstraite qui sert de classe de base pour
la géométrie 3D. Les classes qui dérivent de cette classe de base abstraite
définissent des formes géométriques 3D. La classe d’objets Geometry3D peut
être utilisée pour le test d’atteinte et pour le rendu des données graphiques
3D.
Une géométrie 3D contient les données d’un modèle mais ne sait pas s’afficher
elle-même. Le seul type de géométrie hérité de la classe abstraite Geometry3D
est MeshGeometry3D. La figure 4-5 visualise l’arbre d’héritage de Geometry3D.
Figure 4-5
sens
antihoraire
indice 0
A(0,3,0)
indice 1
B(4,0,0)
X
indice 2
C(0,0,3)
Z
Pour afficher ce triangle, il faut l’intégrer à un Visual3D. En l’occurrence,
nous référençons l’objet MeshGeometry3D dans la propriété Content d’un
Copyright 2011 Patrice REY
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='0 3 0, 4 0 0, 0 0 3'
TriangleIndices='0 2 1'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush='Red'></DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.BackMaterial>
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial>
</GeometryModel3D.BackMaterial>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
sens
antihoraire
indice 0
A(4,2,0)
indice 1
indice 3
D(0,2,3) B(4,0,0)
X
indice 2
C(0,0,3)
sa propriété Brush est fixée à Red (la face avant du rectangle sera colorée en
rouge). Dans la propriété BackMaterial de GeometryModel3D, on ajoute un
objet DiffuseMaterial dont sa propriété Brush est fixée à Yellow (la face arrière
du rectangle sera colorée en jaune).
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
Copyright 2011 Patrice REY
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='4 2 0, 4 0 0, 0 0 3, 0 2 3'
TriangleIndices='2 1 0, 0 3 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush='Red'></DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.BackMaterial>
CHAPITRE 4 □ La scène 3D 107
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial>
</GeometryModel3D.BackMaterial>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
5 - Matière et surface
Une matière est un objet hérité de la classe Material qui définit l’apparence d’une
surface, au moyen d’un pinceau spécifié dans sa propriété Brush (propriété
incluant tous les pinceaux 2D). Les pinceaux évolués contenant une image
ou un objet Visual doivent être appliqués à des modèles gérant le placage de
texture.
La figure 4-8 visualise l’arbre d’héritage de la classe de base abstraite Material.
Figure 4-8
108 La programmation graphique 3D de WPF 4
Les classes dérivées de la classe Material sont:
• DiffuseMaterial qui représente une surface autorisant l’application d’un
pinceau 2D, tel qu’un SolidColorBrush ou un TileBrush, à un modèle 3D
éclairé de manière diffuse.
• EmissiveMaterial qui représente une surface autorisant l’application d’un
Brush à un modèle 3D afin qu’il soit compris dans les calculs de l’éclairage
comme si les surfaces émettaient une lumière égale à la couleur du Brush.
• SpecularMaterial qui représente une surface autorisant l’application d’un
pinceau 2D, tel qu’un SolidColorBrush ou un TileBrush, à un modèle 3D
éclairé de manière spéculaire.
• MaterialGroup qui représente un Material qui forme un élément composite
de la collection des matières.
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='4 2 0, 4 0 0, 0 0 3, 0 2 3'
Copyright 2011 Patrice REY
TriangleIndices='2 1 0, 0 3 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<DiffuseMaterial Brush='Red'></DiffuseMaterial>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
CHAPITRE 4 □ La scène 3D 109
Une surface lumineuse est définit par un objet EmissiveMaterial. Cette surface
lumineuse est indépendante de l’éclairage ambiant. Mais un objet avec une
surface lumineuse n’illumine pas les éléments 3D présents dans son entourage
immédiat.
La classe EmissiveMaterial expose principalement les propriétés suivantes:
• Brush qui représente le pinceau utilisé.
• Color qui définit la couleur réfléchie pour la texture de la matière.
Par exemple, la face d’un rectangle peinte en jaune s’exprimera en XAML par:
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='4 2 0, 4 0 0, 0 0 3, 0 2 3'
TriangleIndices='2 1 0, 0 3 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<EmissiveMaterial Brush='Yellow'></EmissiveMaterial >
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
Les matières peuvent être combinées au sein d’un objet MaterialGroup, formant
alors une matière composite. La propriété Children de MaterialGroup contient
une collection d’objets Material enfants qui constituent la matière composite.
Par exemple, un MaterialGroup permet de définir une surface unie ornée de
motifs lumineux visibles même dans l’ombre. Un objet DiffuseMaterial définit
la surface unie. Un objet DrawingBrush, utilisé par un objet EmissiveMaterial,
définit les motifs lumineux. Cela s’exprimera en XAML par:
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
110 La programmation graphique 3D de WPF 4
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='4 2 0, 4 0 0, 0 0 3, 0 2 3'
TriangleIndices='2 1 0, 0 3 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial >
<EmissiveMaterial>
<EmissiveMaterial.Brush>
<DrawingBrush>
<DrawingBrush.Drawing>
<GeometryDrawing Brush='MediumBlue'>
...
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</EmissiveMaterial.Brush>
</EmissiveMaterial>
</MaterialGroup>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D>
CHAPITRE 4 □ La scène 3D 111
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='4 2 0, 4 0 0, 0 0 3, 0 2 3'
TriangleIndices='2 1 0, 0 3 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial >
<SpecularMaterial SpecularPower='100.0' Brush='White'/>
</MaterialGroup>
</GeometryModel3D.Material>
</GeometryModel3D>
</ModelVisual3D.Content>
</ModelVisual3D>
6 - L’éclairage de la scène
L’éclairage est géré par des objets dérivés de la classe de base abstraite Light.
La figure 4-9 visualise l’arbre d’héritage de la classe Light. WPF gère l’éclairage
par un objet Model3D d’un type particulier, invisible, utilisé pour calculer les
couleurs des éléments de la scène. C’est pour cette raison que l’objet Light
hérite de la classe Model3D.
Figure 4-9
112 La programmation graphique 3D de WPF 4
Les classes dérivées de la clase abstraite Light sont:
• DirectionLight qui représente une lumière qui projette son effet dans une
direction spécifiée par un Vector3D.
• AmbientLight qui représente une lumière qui applique uniformément de la
lumière sur les objets, indépendamment de leur forme.
• PointLightBase qui est une classe de base abstraite qui représente un objet
lumière comportant une position dans l’espace et projetant sa lumière
dans toutes les directions.
La classe PointLightBase possède comme classes dérivées les classes:
• PointLight qui représente une source de lumière qui a une position spécifiée
dans l’espace et qui projette sa lumière dans toutes les directions.
• SpotLight qui représente une lumière qui projette son effet dans une zone
conique selon une direction spécifiée.
Tous ces objets représentant un éclairage, expose la propriété Color qui définit
la couleur de l’éclairage.
L’objet AmbientLight produit un éclairage uniforme, sans ombre, avec une
position indéterminée.
L’objet DirectionalLight produit un éclairage similaire aux rayons solaires. Les
rayons sont parallèles comme s’ils provenaient d’une source très éloignée.
Sa position d’éclairage est indéterminée. Sa propriété Direction détermine
l’orientation des rayons lumineux au moyen d’un vecteur. Généralement la
puissance de l’éclairage directionnel est fixe. La seule façon de l’augmenter
consiste à positionner plusieurs objets DirectionLight.
Les objets PointLight et SpotLight produisent un éclairage similaire à une
lampe d’intérieur qui rayonne autour d’une position donnée. Ces deux objets
exposent principalement les propriétés suivantes:
• Position qui définit l’emplacement de la source d’éclairage (une position de
type Point3D).
• Range qui représente la portée maximale de la source d’éclairage; la portée
a une valeur infinie par défaut; les zones en dehors de cette portée ne sont
Copyright 2011 Patrice REY
pas éclairées (dans le cas d’une valeur finie pour la portée maximale).
• ConstantAttenuation qui représente une valeur de constante par laquelle
l’intensité de la lumière diminue sur la distance; en agissant comme un
facteur de division, la luminosité de la lampe peut être réduite.
• LinearAttenuation qui représente une valeur qui spécifie la diminution
linéaire de l’intensité de la lumière sur la distance; en agissant comme un
facteur multiplicateur de la distance, la lumière peut être réduite.
CHAPITRE 4 □ La scène 3D 113
• QuadraticAttenuation qui représente une valeur qui spécifie la diminution de
l’effet de la lumière sur la distance, calculée par une opération quadratique;
en agissant comme facteur multiplicateur du carré de la distance, la lumière
peut être réduite.
Si l’objet PointLight rayonne dans toutes les directions, l’objet SpotLight, quant à
lui, réduit son éclairage à un cône, en exposant des propriétés supplémentaires
qui sont:
• Direction qui définit un Vector3D qui spécifie la direction dans laquelle
l’objet SpotLight projette sa lumière.
• OuterConeAngle qui définit un angle qui spécifie la proportion d’une
projection conique d’un objet SpotLight en dehors de laquelle la lumière
n’éclaire pas les objets dans la scène.
• InnerConeAngle qui définit un angle qui spécifie la proportion d’une
projection conique d’un objet SpotLight dans laquelle la lumière éclaire
entièrement les objets dans la scène.
7 - Les caméras
Figure 4-10
Figure 4-11
FarPlaneDistance
NearPlaneDistance
UpDirection
Y
LookDirection
Z
X FieldOfView
centre de
projection
(caméra)
plan de
projection volume
d’observation
plan de loin
FarPlaneDistance
NearPlaneDistance
plan de
projection
UpDirection Width
Y
LookDirection
Z
X
centre de
projection à
l’infini (caméra)
plan de volume
près d’observation plan de
loin
1 - La modélisation basique
Pour que la zone de rendu affiche quelque chose, il faut ajouter une caméra
(propriété Camera du Viewport3D). Pour cela on ajoute une caméra x_camera
à projection perspective, de type PerspectiveCamera. On fixe sa position dans
l’espace 3D (Position = «0,0,5»), son axe de visée est représenté par un vecteur
(LookDirection = «0,0,-5»), sa direction vers le haut est représentée par un
vecteur (UpDirection = «0 1 0») et l’angle de vue est fixé à 60 (FieldOfView =
«60»). Pour que la caméra fixe en permanence l’origine du repère 3D, il suffit
de fixer l’orientation de la visée par le vecteur qui va du point (0,0,5) qui
correspond à la position de la caméra, au point (0,0,0) qui est l’origine. Cela
se traduit donc par un Vector3D(0,0,-5) pour une position de caméra de (0,0,5)
soit le signe opposé aux trois coordonnées de la caméra. Le UpDirection de la
caméra est celui de l’axe Y pour signifier aucune inclinaison de la caméra. Son
inclinaison est donc représentée par le Vector3D(0,1,0).
<ContainerUIElement3D>
<ModelUIElement3D>
<Model3DGroup x:Name='x_objets_3d'>
<Model3DGroup x:Name='x_lumiere'>
<AmbientLight Color='White'></AmbientLight>
</Model3DGroup>
...
</Model3DGroup>
CHAPITRE 5 □ Les modèles 3D basiques 121
</ModelUIElement3D>
</ContainerUIElement3D>
(0,2,0) indice 2
angle 30°
(0,0,5) caméra
Z
<ContainerUIElement3D>
<ModelUIElement3D>
<Model3DGroup x:Name='x_objets_3d'>
...
<Model3DGroup x:Name='x_triangle'>
<GeometryModel3D x:Name='x_triangle_geomodel3d'>
<GeometryModel3D.Material>
<DiffuseMaterial x:Name='x_surface_1'
Brush='DodgerBlue'>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.BackMaterial>
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial>
</GeometryModel3D.BackMaterial>
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions='0 0 0, 2 0 0, 0 2 0'
TriangleIndices='0 1 2'></MeshGeometry3D>
</GeometryModel3D.Geometry>
</GeometryModel3D>
...
</Model3DGroup>
</Model3DGroup>
</ModelUIElement3D>
</ContainerUIElement3D>
<ContainerUIElement3D>
<ModelUIElement3D>
<Model3DGroup x:Name='x_objets_3d'>
...
<Model3DGroup x:Name='x_triangle'>
CHAPITRE 5 □ Les modèles 3D basiques 123
...
<Model3DGroup.Transform>
<Transform3DGroup>
<RotateTransform3D
x:Name='x_triangle_rotation_xz'>
</RotateTransform3D>
<RotateTransform3D
x:Name='x_triangle_rotation_yz'>
</RotateTransform3D>
</Transform3DGroup>
</Model3DGroup.Transform>
</Model3DGroup>
</ModelUIElement3D>
</ContainerUIElement3D>
//radiobutton surfaces
private void x_surface_Checked(object sender, RoutedEventArgs e) {
RadioButton rbtn = (RadioButton)sender;
if (rbtn.Name == «x_rbtn_surface_1») {
Copyright 2011 Patrice REY
Figure 5-4
propriété
OuterConeAngle
propriété InnerConeAngle propriété
Direction
126 La programmation graphique 3D de WPF 4
//radiobutton lumiere
private void x_rbtn_eclairage_Checked(object sender,
RoutedEventArgs e) {
RadioButton rbtn = (RadioButton)sender;
if (rbtn.Name == «x_rbtn_eclairage_1») {
x_lumiere.Children.Clear();
AmbientLight amb = new AmbientLight(Colors.White);
x_lumiere.Children.Add(amb);
}
if (rbtn.Name == «x_rbtn_eclairage_2») {
x_lumiere.Children.Clear();
SpotLight spot = new SpotLight();
spot.Color = Colors.White;
spot.Position = new Point3D(0, 0, 5);
spot.Direction = new Vector3D(0, 0, -5);
spot.InnerConeAngle = 5;
spot.OuterConeAngle = 10;
x_lumiere.Children.Add(spot);
}
}
<Viewport3D.Camera>
<PerspectiveCamera x:Name='x_camera' Position='0,10,10'
LookDirection='0,-10,-10' FieldOfView='60'
UpDirection='0 1 0'>
<PerspectiveCamera.Transform>
<RotateTransform3D x:Name='x_cam_rotate_y'></RotateTransform3D>
</PerspectiveCamera.Transform>
CHAPITRE 5 □ Les modèles 3D basiques 127
Figure 5-5
128 La programmation graphique 3D de WPF 4
</PerspectiveCamera>
</Viewport3D.Camera>
//rotation de la camera
private void x_slider_cam_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
if (this.IsLoaded == true) {
x_cam_rotate_y.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 1, 0), x_slider_cam.Value);
x_info_cam_angle.Text = «Caméra: angle = « +
Math.Round(x_slider_cam.Value, 2).ToString();
}
}
<Model3DGroup>
<GeometryModel3D x:Name='x_rectangle'>
...
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions='0 0 0, 0 0 4, 6 0 4, 6 0 0'
TriangleIndices='0 1 2, 2 3 0'
TextureCoordinates='0 0, 0 1, 1 1, 1 0'></MeshGeometry3D>
Copyright 2011 Patrice REY
</GeometryModel3D.Geometry>
</GeometryModel3D>
...
</Model3DGroup>
<Model3DGroup>
<GeometryModel3D x:Name='x_rectangle'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;
component/contenu/image/texture-avant.png'>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.BackMaterial>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;
component/contenu/image/texture-arriere.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.BackMaterial>
<GeometryModel3D.Geometry>
<MeshGeometry3D
Positions='0 0 0, 0 0 4, 6 0 4, 6 0 0'
TriangleIndices='0 1 2, 2 3 0'
TextureCoordinates='0 0, 0 1, 1 1, 1 0'></MeshGeometry3D>
</GeometryModel3D.Geometry>
</GeometryModel3D>
...
</Model3DGroup>
Le bas de la figure 5-5 montre des copies d’écran où l’on voit bien le rectangle
Copyright 2011 Patrice REY
2 - Le cube
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='0 0 1, 1 0 1, 1 1 1, 0 1 1,
0 1 0, 1 1 0, 1 0 0, 0 0 0' >
</MeshGeometry3D>
...
</GeometryModel3D.Geometry>
Puis, en prenant chaque face et en tournant dans le sens inverse des aiguilles
d’une montre, on indique les sommets des triangles qui composent la face. La
figure 5-9 visualise la face avant qui est composée des points 3(0,1,1), 0(0,0,1),
1(1,0,1) et 2(1,1,1). Cela nous donne le premier triangle qui serait «1 2 3» et le
deuxième triangle qui serait «3 0 1».
132 La programmation graphique 3D de WPF 4
Figure 5-7
4 (0,1,0) 5 (1,1,0)
5 (1,1,0) 4 (0,1,0)
3 (0,1,1) 2 (1,1,1)
6 (1,0,0) 7 (0,0,0)
7 (0,0,0) 6(1,0,0)
X
Z
1 (1,0,1) 6(1,0,0)
0 (0,0,1) 1 (1,0,1)
3 (0,1,1) 2 (1,1,1)
En faisant cela pour les 6 faces, on obtient l’ordre des indices des triangles
que l’on affecte à la propriété TriangleIndices du MeshGeometry3D. Ainsi la
géométrie du cube est complètement réalisée.
134 La programmation graphique 3D de WPF 4
Figure 5-9
3 (0,1,1) 2 (1,1,1)
0 (0,0,1) 1 (1,0,1)
<GeometryModel3D.Geometry>
<MeshGeometry3D Positions='0 0 1, 1 0 1, 1 1 1, 0 1 1,
0 1 0, 1 1 0, 1 0 0, 0 0 0'
TriangleIndices='
1 2 3, 3 0 1,
6 5 2, 2 1 6,
7 4 5, 5 6 7,
0 3 4, 4 7 0,
2 5 4, 4 3 2,
6 1 0, 0 7 6' >
</MeshGeometry3D>
</GeometryModel3D.Geometry>
<GeometryModel3D.Transform>
<Transform3DGroup>
<RotateTransform3D
x:Name='x_cube_rotate_y'></RotateTransform3D>
<RotateTransform3D
x:Name='x_cube_rotate_x'></RotateTransform3D>
<RotateTransform3D
x:Name='x_cube_rotate_z'></RotateTransform3D>
</Transform3DGroup>
Copyright 2011 Patrice REY
</GeometryModel3D.Transform>
Pour appliquer une texture sur tout le cube à partir d’une image, on instancie
un DiffuseMaterial et on affecte à sa propriété Brush, un objet ImageBrush dont
sa propriété ImageSource pointe vers une ressource texture-2.png qui se trouve
dans le sous-dossier image du dossier contenu. Pour que cette texture s’affiche, il
faut que la propriété TextureCoordinates de MeshGeometry3D soit explicitement
exprimée. On indique alors un relevé des coordonnées à mapper.
Le bas de la figure 5-7 montre des copies d’écran quand le cube tourne en
fonction des modifications opérées sur les glissières.
Un cube est un objet solide qui possède 6 faces. Ces 6 faces sont déterminées
à partir de 8 points. Comme le visualise la figure 5-11, si nous considérons
que le côté d’un cube a une longueur a, alors nous pouvons exprimer les 8
points en fonction de a. De ce fait, les 8 points avec les 8 indices représentent
les positions dans l’espace 3D d’un cube avec une longueur de côté égale à a.
A partir de là, nous pouvons établir les 6 faces avec leurs indices (la face avant
avec les indices 0, 1, 2 et 3, la face droite avec les indices 4, 5, 1 et 0, etc.).
Pour créer le cube et l’ajouter au Viewport3D, nous utilisons la méthode
AjouterCube(). Elle consiste à instancier un Model3DGroup intitulé groupe_cube,
qui réalise le cube dans son intégralité. Puis ce groupe_cube est ajouté aux
enfants de x_scene_3d (propriété Children) par la méthode Add(..).
Pour modéliser le cube dans son intégralité, on utilise la méthode
CreerCubeAvecLargeurCote(..) en lui passant comme paramètres, une longueur
de côté (1.5) et une liste contenant les chemins pointant vers les ressources
image des faces, stockées dans le sous-dossier image du dossier contenu.
liste_chemin_texture.Add(«contenu/image/face-cube-dessus.png»);
liste_chemin_texture.Add(«contenu/image/face-cube-dessous.png»);
Model3DGroup groupe_cube =
CreerCubeAvecLargeurCote(1.5, liste_chemin_texture);
...
x_scene_3d.Children.Add(groupe_cube);
}
CHAPITRE 5 □ Les modèles 3D basiques 139
Figure 5-11
Y
6 (0,a,0) 5 (a,a,0)
2 (0,a,a) 1 (a,a,a)
7 (0,0,0) 4(a,0,0)
X
3 (0,0,a) 0 (a,0,a)
Z
face avant face droite face arrière face gauche face dessus face dessous
pt3 pt0
(0,1) (1,1)
Les transformations de rotation sont ajoutées par code procédural. Pour cela,
on déclare des données privées m_cube_rotate_x, m_cube_rotate_y et m_cube_
rotate_z de type RotateTransform3D. On instancie un gp_transform de type
CHAPITRE 5 □ Les modèles 3D basiques 143
Transform3DGroup, et on ajoute à ses enfants les trois objets RotateTransform3D.
La propriété Transform de groupe_cube reçoit le groupe des transformations
gp_transform.
Le bas de la figure 5-10 montre des copies d’écran dans lesquelles on voit les
faces du cube après diverses rotations.
2 (-d,e/2,-e/2) 5 (d,e/2,-e/2)
1 (-d,e/2,e/2) 6 (d,e/2,e/2)
X
3 (-d,-e/2,-e/2) 4 (d,-e/2,-e/2)
Copyright 2011 Patrice REY
0 (-d,-e/2,e/2) Z 7 (d,-e/2,e/2)
AjouterIndicesFace(6, 5, 2, 1, maillage.TriangleIndices);
//face dessous
AjouterIndicesFace(0, 3, 4, 7, maillage.TriangleIndices);
return maillage;
}
gp_transform.Children.Add(m_scene_rotate_x);
m_scene_rotate_y = new RotateTransform3D();
gp_transform.Children.Add(m_scene_rotate_y);
m_scene_rotate_z = new RotateTransform3D();
gp_transform.Children.Add(m_scene_rotate_z);
x_scene_3d.Transform = gp_transform;
}
CHAPITRE 5 □ Les modèles 3D basiques 149
Enfin il suffit de relier les modifications des valeurs sur les glissières aux
transformations adéquates par l’intermédiaire du gestionnaire d’événements
x_slider_ValueChanged(..).
Le bas de la figure 5-13 montre des copies d’écran dans lesquelles on voit la
scène 3D, composée d’un cube et des 3 axes des coordonnées, après diverses
rotations.
3 - La sphère
Figure 5-16 Y
r
θ
φ
O X
( i+1 , j )
(i,j)
( i+1 , j+1 )
( i , j+1 )
//usercontrol chargé
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
this.UpdateLayout();
AfficherInfosDansGrille();
AjouterRepereXYZ();
Copyright 2011 Patrice REY
AjouterSceneRotation();
CreerSphere(new Point3D(0, 0, 0), 1.5, 20, 15);
}
4 - Le cylindre
D2
D1 B2
B1
C2
C1
A2
A1
Y B D
B x=Rg*cos(β) x=Rp*cos(β)
y=H y=H
z=-Rg*sin(β) z=-Rp*sin(β)
A C
x=Rg*cos(β) x=Rp*cos(β)
y=0 y=0
A z=-Rg*sin(β) z=-Rp*sin(β)
β X
Ensuite il faut dessiner les faces. Un parcours des listes de points permet
d’extraire les points nécessaires à chaque face à dessiner (face extérieure, face
intérieure, face de dessus et face de dessous).
p[0] = liste_pt_ext_bas[xx];
p[1] = liste_pt_ext_haut[xx];
p[2] = liste_pt_ext_haut[xx+1];
p[3] = liste_pt_ext_bas[xx+1];
DessinerFaceCylindrique(p[0], p[1], p[2], p[3], gp_cylindre);
}
//face interieur
for (int xx = 0; xx < nb_pt - 1; xx++) {
CHAPITRE 5 □ Les modèles 3D basiques 159
Point3D[] p = new Point3D[4];
p[0] = liste_pt_int_bas[xx];
p[1] = liste_pt_int_haut[xx];
p[2] = liste_pt_int_haut[xx + 1];
p[3] = liste_pt_int_bas[xx + 1];
DessinerFaceCylindrique(p[3], p[2], p[1], p[0], gp_cylindre);
}
//face dessus
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[4];
p[0] = liste_pt_ext_haut[xx];
p[1] = liste_pt_int_haut[xx];
p[2] = liste_pt_int_haut[xx + 1];
p[3] = liste_pt_ext_haut[xx + 1];
DessinerFaceCylindrique(p[0], p[1], p[2], p[3], gp_cylindre);
}
//face dessous
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[4];
p[0] = liste_pt_ext_bas[xx];
p[1] = liste_pt_int_bas[xx];
p[2] = liste_pt_int_bas[xx + 1];
p[3] = liste_pt_ext_bas[xx + 1];
DessinerFaceCylindrique(p[3], p[2], p[1], p[0], gp_cylindre);
}
}
5 - Le cône
Le calcul des sommets des facettes se fait de la même manière que pour le
cylindre. Comme le montre la figure 5-22, il suffit de calculer les points qui
sont sur la couronne extérieure de la base ainsi que les points qui sont sur
la couronne extérieure de la pointe. Dans le cas général, une facette à deux
triangles doit être dessinée (constituée des points A1, A2, B1 et B2), puis
une facette triangulaire constituée des points D, B1 et B2, enfin une facette
triangulaire composée des points C, A1 et A2.
Il faut ajouter une valeur qui représente le nombre de divisions. Quand ce
nombre est égale à 4, on sera en présence d’une pyramide. Avec un nombre de
36, on aura une bonne division en triangles pour une représentation texturée.
La création du cône se fait par la méthode AjouterCone(..) par laquelle on
précise le diamètre de la base, le diamètre de la pointe, la hauteur du cône et
un nombre de divisions. La liste liste_pt_base calcule les points sur la couronne
extérieure de la base et cela en fonction du nombre de divisions. La liste liste_
pt_pointe calcule les points sur la couronne extérieure de la pointe en fonction
du nombre de divisions.
return;
}
Model3DGroup gp_cone = new Model3DGroup();
x_cones.Children.Add(gp_cone);
double rayon_base = dia_base / 2;
double ray_pointe = dia_pointe / 2;
List<Point3D> liste_pt_base = new List<Point3D>();
for (int angle = 0; angle <= 360; angle += 360 / division) {
CHAPITRE 5 □ Les modèles 3D basiques 163
Figure 5-22
D
B2
B1
C A2
A1
Y B D
x=Rg*cos(β) x=Rp*cos(β)
y=H y=H
z=-Rg*sin(β) z=-Rp*sin(β)
B A C
x=Rg*cos(β) x=Rp*cos(β)
y=0 y=0
z=-Rg*sin(β) z=-Rp*sin(β)
A
β X
Ensuite un parcours des listes permet de récupérer les sommets des facettes.
Sur la surface extérieure, la facette sera composée de deux triangles qui seront
assemblés et dessinés par la méthode DessinerFaceDeuxTriangles(..). Pour la
base, la méthode DessinerFaceUnTriangle(..) dessine un triangle en fonction des
points. Si la pointe a un diamètre non nul, cette même méthode est appelée de
nouveau pour dessiner les triangles du haut du cône coupé.
//face dessus
if (dia_pointe != 0) {
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[3];
p[0] = liste_pt_pointe[xx];
p[1] = liste_pt_pointe[xx + 1];
p[2] = new Point3D(0, hauteur, 0);
DessinerFaceUnTriangle(p[0], p[1], p[2], gp_cone); } } }
CHAPITRE 5 □ Les modèles 3D basiques 165
Un sélecteur x_select, de type ComboBox, permet de sélectionner un type de
cône à afficher. La figure 5-20 visualise les différents cônes générés. Le premier
cône est du type classique et il est généré par la méthode AjouterCone(4, 0, 2,
36). Le deuxième cône est du type classique mais coupé sur le haut et il est
généré par la méthode AjouterCone(4, 2, 2, 36). Le troisième cône est du type
classique mais coupé sur le bas et il est généré par la méthode AjouterCone(2, 4,
2, 36). Le quatrième cône est du type pyramide et il est généré par la méthode
AjouterCone(4, 0, 2, 4). Le cinquième cône est du type pyramide mais coupé sur
le haut et il est généré par la méthode AjouterCone(4, 2, 2, 4). Le sixième cône
est du type pyramide mais coupé sur le bas et il est généré par la méthode
AjouterCone(2, 4, 2, 4).
//selecteur cone
private void x_select_SelectionChanged(object sender, SelectionChan-
gedEventArgs e) {
if (this.IsLoaded == true) {
x_cones.Children.Clear();
RemiseAZeroTransform();
switch (x_select.SelectedIndex) {
case 0:
AjouterCone(4, 0, 2, 36);
break;
case 1:
AjouterCone(4, 2, 2, 36);
break;
case 2:
AjouterCone(2, 4, 2, 36);
break;
case 3:
AjouterCone(4, 0, 2, 4);
break;
case 4:
AjouterCone(4, 2, 2, 4);
break;
case 5:
AjouterCone(2, 4, 2, 4);
break;
}
}
}
166 La programmation graphique 3D de WPF 4
6 - Le tore
}
Point3D[] p = new Point3D[4];
for (int i = 0; i < N - 1; i++) {
for (int j = 0; j < n - 1; j++) {
p[0] = pts[i, j];
p[1] = pts[i + 1, j];
p[2] = pts[i + 1, j + 1];
p[3] = pts[i, j + 1];
CHAPITRE 5 □ Les modèles 3D basiques 167
Figure 5-23
168 La programmation graphique 3D de WPF 4
Figure 5-24
β A
X
B
A B
Z x=Rg*cos(β) x=Rp*cos(β)
y=0 y=0
z=-Rg*sin(β) z=-Rp*sin(β)
}
x_tores.Children.Add(gp_tore);
}
//selecteur tore
private void x_select_SelectionChanged(object sender,
SelectionChangedEventArgs e) {
if (this.IsLoaded == true) {
x_tores.Children.Clear();
RemiseAZeroTransform();
switch (x_select.SelectedIndex) {
case 0:
AjouterTore(new Point3D(0, 0, 0), 1.6, 0.8, 20, 15);
break;
case 1:
AjouterTore(new Point3D(0, 0, 0), 1, 1, 20, 15);
break;
case 2:
AjouterTore(new Point3D(0, 0, 0), 0.6, 1.6, 20, 15);
break;
}
}
}
Nous avons vu que les géométries des modèles 3D pouvaient être exprimées
en XAML et en code procédural. Très souvent, il s’avère pratique de pouvoir
modéliser en XAML tout comme en code procédural, un modèle 3D avec des
propriétés spécifiques déterminées à l’avance et en fonction d’une utilisation
ultérieure. Cette façon de faire passe par une personnalisation des objets 3D
avec pour but une réutilisation à volonté, rapide et précise.
Ce chapitre est consacré à la façon de créer ces modèles personnalisés et
réutilisables.
Ces variables servent à définir les propriétés publiques du cube (en lecture et
écriture) qui sont Largeur, Hauteur, Profondeur et Centre.
172 La programmation graphique 3D de WPF 4
Figure 6-1
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit la
géométrie du cube, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques.
//constructeur
public GeoCube() {
m_profondeur = 1.0;
m_largeur = 1.0;
m_hauteur = 1.0;
m_centre = new Point3D();
}
...
Point3D[] pts = new Point3D[8];
double hl = 0.5 * Profondeur;
double hw = 0.5 * Largeur;
double hh = 0.5 * Hauteur;
pts[0] = new Point3D(hl, hh, hw);
pts[1] = new Point3D(hl, hh, -hw);
pts[2] = new Point3D(-hl, hh, -hw);
pts[3] = new Point3D(-hl, hh, hw);
pts[4] = new Point3D(-hl, -hh, hw);
pts[5] = new Point3D(-hl, -hh, -hw);
pts[6] = new Point3D(hl, -hh, -hw);
pts[7] = new Point3D(hl, -hh, hw);
for (int i = 0; i < 8; i++) {
pts[i] += (Vector3D)Centre;
}
...
//dessus (0-4)
for (int i = 0; i < 4; i++) {
mesh.Positions.Add(pts[i]);
}
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
mesh.TriangleIndices.Add(2);
CHAPITRE 6 □ Les géométries 3D personnalisées 175
mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(0);
//dessous (4-7):
for (int i = 4; i < 8; i++) {
mesh.Positions.Add(pts[i]);}
mesh.TriangleIndices.Add(4);
mesh.TriangleIndices.Add(5);
mesh.TriangleIndices.Add(6);
mesh.TriangleIndices.Add(6);
mesh.TriangleIndices.Add(7);
mesh.TriangleIndices.Add(4);
//devant (8-11):
mesh.Positions.Add(pts[0]);
mesh.Positions.Add(pts[3]);
mesh.Positions.Add(pts[4]);
mesh.Positions.Add(pts[7]);
mesh.TriangleIndices.Add(8);
mesh.TriangleIndices.Add(9);
mesh.TriangleIndices.Add(10);
mesh.TriangleIndices.Add(10);
mesh.TriangleIndices.Add(11);
mesh.TriangleIndices.Add(8);
...
//texture face 1
mesh.TextureCoordinates.Add(new Point(1, 0));
mesh.TextureCoordinates.Add(new Point(1, 1));
mesh.TextureCoordinates.Add(new Point(0, 1));
mesh.TextureCoordinates.Add(new Point(0, 0));
//...
Maintenant nous allons utiliser cette classe personnalisée pour créer différents
cubes. Il faut commencer par ajouter, dans le fichier ExGeometrieCube.xaml,
une référence à l’espace de noms, préfixée par visuel, qui est xmlns:visuel =
«clr-namespace:Prog3dWpf4.chapitre06», qui permettra l’appel à la classe
personnalisée.
De façon à réutiliser plusieurs fois le maillage, on ajoute dans les ressources
de l’UserControl (<UserControl.Resources>) une ressource intitulée r_geo_cube,
qui définit un maillage de base avec les propriétés publiques implémentées
176 La programmation graphique 3D de WPF 4
(Centre, Largeur, Hauteur et Profondeur).
</Model3DGroup>
La figure 6-1 visualise l’ensemble des cubes générés avec les 3 axes de
coordonnées X, Y et Z, colorés respectivement en rouge, bleu et vert.
Ces variables servent à définir les propriétés publiques de la sphère (en lecture
et écriture) qui sont LongueurX, LongueurY, LongueurZ, DivisionTheta, DivisionPhi
et Centre.
//proprietes publiques
public double LongueurX {
get { return m_longueur_x; }
set { m_longueur_x = value; }
}
public double LongueurY {
get { return m_longueur_y; }
set { m_longueur_y = value; }
}
public double LongueurZ {
get { return m_longueur_z; }
set { m_longueur_z = value; }
}
public int DivisionTheta {
get { return m_division_theta; }
set { m_division_theta = value; }
}
public int DivisionPhi {
get { return m_division_phi; }
set { m_division_phi = value; }
}
public Point3D Centre {
get { return m_centre; }
set { m_centre = value; }
Copyright 2011 Patrice REY
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit
la géométrie de la sphère, en retournant cette géométrie par l’appel de la
méthode ObtenirMaillage3D().
CHAPITRE 6 □ Les géométries 3D personnalisées 181
//propriete pour obtenir le maillage
public MeshGeometry3D Maillage {
get { return ObtenirMaillage3D(); }
}
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques.
//constructeur
public GeoSphere() {
m_longueur_x = 1.0;
m_longueur_y = 1.0;
m_longueur_z = 1.0;
m_division_theta = 30;
m_division_phi = 20;
m_centre = new Point3D();
}
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}
<Model3DGroup x:Name='x_sphere_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_sphere},
Path=Maillage}'>
<GeometryModel3D.Material>
<MaterialGroup>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_sphere/texture-sphere-01.png'></ImageBrush>
</DiffuseMaterial.Brush>
184 La programmation graphique 3D de WPF 4
</DiffuseMaterial>
</MaterialGroup>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<TranslateTransform3D OffsetX='-2'></TranslateTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
...
}
Ces variables servent à définir les propriétés publiques du cylindre (en lecture
et écriture) qui sont RayonInterne, RayonExterne, Hauteur, DivisionTheta et Centre.
CHAPITRE 6 □ Les géométries 3D personnalisées 185
Figure 6-3
186 La programmation graphique 3D de WPF 4
//proprietes publiques lecture ecriture
public double RayonInterne {
get { return m_rayon_int; }
set { m_rayon_int = value; }
}
public double RayonExterne {
get { return m_rayon_ext; }
set { m_rayon_ext = value; }
}
public double Hauteur {
get { return m_hauteur; }
set { m_hauteur = value; }
}
public int DivisionTheta {
get { return m_division_theta; }
set { m_division_theta = value; }
}
public Point3D Centre {
get { return m_centre; }
set { m_centre = value; }
}
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit la
géométrie du cylindre, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques. Copyright 2011 Patrice REY
//constructeur
public GeoCylindre() {
m_rayon_int = 1.0;
m_rayon_ext = 1.0;
m_hauteur = 1.0;
m_division_theta = 20;
m_centre = new Point3D();
}
CHAPITRE 6 □ Les géométries 3D personnalisées 187
La méthode privée ObtenirMaillage3D() définit la géométrie du cylindre
et permet à la propriété Maillage d’obtenir cette géométrie en vue de son
affichage.
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}
<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Largeur='1' Hauteur='1'
Profondeur='1' Centre='0,0,0'></visuel:GeoCube>
<visuel:GeoCylindre x:Key='r_geo_cylindre' RayonExterne='1'
RayonInterne='0.5' Hauteur='1' Centre='0,0,0'></visuel:GeoCylindre>
<visuel:GeoCylindre x:Key='r_geo_anneau' RayonExterne='2'
RayonInterne='1.5' Hauteur='0.2' Centre='0,0,0'>
CHAPITRE 6 □ Les géométries 3D personnalisées 189
</visuel:GeoCylindre>
<visuel:GeoCylindre x:Key='r_geo_cylindre_plein' RayonExterne='1'
RayonInterne='0' Hauteur='1' Centre='0,0,0'></visuel:GeoCylindre>
</UserControl.Resources>
<Model3DGroup x:Name='x_cylindre_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_cylindre},
Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cylindre/texture-cylindre-01.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='0.5'
ScaleY='2' ScaleZ='0.5'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
Ces variables servent à définir les propriétés publiques du cône (en lecture et
écriture) qui sont RayonDessus, RayonDessous, Hauteur, DivisionTheta et Centre.
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit la
géométrie du cône, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques.
//constructeur
public GeoCone() {
m_rayon_dessus = 1.0;
m_rayon_dessous = 1.0;
m_hauteur = 1.0;
m_division_theta = 20;
m_centre = new Point3D();
}
//definition du maillage
Copyright 2011 Patrice REY
<Model3DGroup x:Name='x_cone_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_cone_classique},
Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cone/texture-cone-01.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<TranslateTransform3D
OffsetY='0.5' OffsetX='-2'></TranslateTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
196 La programmation graphique 3D de WPF 4
La figure 6-4 visualise l’ensemble des cônes générés et positionnés, avec les 3
axes de coordonnées X, Y et Z.
Ces variables servent à définir les propriétés publiques du tore (en lecture et
en écriture) qui sont Rayon1, Rayon2, DivisionH, DivisionV et Centre.
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit
la géométrie du tore, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques.
//constructeur
public GeoTore() {
m_rayon_1 = 1.0;
m_rayon_2 = 0.3;
m_division_h = 20;
m_division_v = 20;
m_centre = new Point3D();
}
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
CHAPITRE 6 □ Les géométries 3D personnalisées 199
return mesh;
}
<Model3DGroup x:Name='x_tore_1'>
Copyright 2011 Patrice REY
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_tore_1},
Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
CHAPITRE 6 □ Les géométries 3D personnalisées 201
personalise_tore/texture-tore-04.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='1'
ScaleY='1' ScaleZ='1'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
La figure 6-5 visualise l’ensemble des tores générés et positionnés, avec les 3
axes de coordonnées X, Y et Z.
1
avec LongueurCote qui est la valeur d’un côté :
2 phi = LongueurCote * (1 + Math.Sqrt(5.0))/2
12
11 (LongueurCote, 0, phi);
(-LongueurCote, 0, phi);
6 (0, phi, LongueurCote);
(0, -phi, LongueurCote);
(phi, LongueurCote, 0);
7 (-phi, LongueurCote, 0);
9 10 (-phi, -LongueurCote, 0);
3 (phi, -LongueurCote, 0);
(0, phi, -LongueurCote);
(0, -phi, -LongueurCote);
(LongueurCote, 0, -phi);
8 (-LongueurCote, 0, -phi);
5
4
//proprietes publiques
public double LongueurCote {
get { return m_longueur_cote; }
set { m_longueur_cote = value; }
}
public Point3D Centre {
get { return m_centre; }
set { m_centre = value; }
}
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit
la géométrie de l’icosaèdre, en retournant cette géométrie par l’appel de la
méthode ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
204 La programmation graphique 3D de WPF 4
les propriétés publiques.
//constructeur
public GeoIcosaedre() {
m_longueur_cote = 1.0;
m_centre = new Point3D();
}
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}
//proprietes publiques
public double LongueurCote {
CHAPITRE 6 □ Les géométries 3D personnalisées 209
get { return m_longueur_cote; }
set { m_longueur_cote = value; }
}
public Point3D Centre {
get { return m_centre; }
set { m_centre = value; }
}
Une autre propriété indispensable, en lecture seule, est Maillage. Elle définit
la géométrie du dodécaèdre, en retournant cette géométrie par l’appel de la
méthode ObtenirMaillage3D().
Le constructeur initialise les variables des champs privés qui sont utilisées par
les propriétés publiques.
//constructeur
public GeoDodecaedre() {
m_longueur_cote = 1.0;
m_centre = new Point3D();
}
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}
mesh.Positions.Add(pts[6]);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
mesh.TriangleIndices.Add(2);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(2);
CHAPITRE 6 □ Les géométries 3D personnalisées 211
mesh.TriangleIndices.Add(3);
mesh.TriangleIndices.Add(4);
// Face 2 (0,1,4,7,3):
...
1 - Hériter de UIElement3D
UIElement3D est une classe de base pour les implémentations au niveau du noyau
WPF, reposant sur les éléments WPF et les caractéristiques de présentation de
base. UIElement3D est une classe de base abstraite à partir de laquelle vous
pouvez dériver des classes pour représenter des éléments 3D spécifiques. La
majeure partie du comportement d’entrée, de mise en focus et de traitement
d’événements pour des éléments 3D est généralement définie dans la classe
UIElement3D. Cela inclut les événements liés à l’entrée au niveau du clavier,
de la souris et du stylet, ainsi que les propriétés d’état qui s’y rattachent. Ces
événements correspondent souvent à des événements routés et de nombreux
événements associés à l’entrée présentent aussi bien une version de routage
par propagation qu’une version par tunneling. Ces événements doubles sont
en général les événements les plus intéressants pour contrôler des objets.
L’objet UIElement3D présente les fonctionnalités suivantes définies spécialement
par la classe UIElement3D :
• il peut répondre à l’entrée de l’utilisateur (y compris contrôler la destination
d’envoi de l’entrée à travers la gestion d’événements de routage ou le
routage de commandes).
• il peut déclencher des événements routés qui suivent un itinéraire à travers
l’arborescence logique d’éléments.
L’UserControl ExUIElementCylindre.xaml, dans le dossier chapitre07, permet
de visualiser un contrôle, qui hérite de UIElement3D, de façon à lui ajouter
de l’interactivité (figure 7-1). Le contrôle réalisé possède alors des propriétés
de dépendance spécifiques ajoutées, et en plus il réagit aux entrées (clavier,
214 La programmation graphique 3D de WPF 4
souris, etc.) et aux événements dans l’espace 3D, tout comme le ferait un
contrôle 2D dans l’espace 2D.
La démarche consiste à créer un contrôle qui hérite de UIElement3D, et à lui
ajouter ses propriétés de dépendance spécifiques. Pour cela nous créons une
nouvelle classe intitulée UICylindre, qui hérite de UIElement3D. On redéfinit la
méthode héritée OnUpdateModel() qui participe aux opérations de rendu en
cas de substitution dans une classe dérivée.
Lorsque vous dérivez une classe à partir de la classe UIElement3D, vous pouvez
utiliser cette méthode en combinaison avec la méthode InvalidateModel()
pour actualiser le modèle de l’élément. Vous devez uniquement appeler
cette méthode dans des scénarios avancés. C’est notamment le cas si la
classe dérivée possède plusieurs propriétés qui affectent l’apparence et que
vous ne souhaitez mettre à jour le modèle sous-jacent qu’une seule fois. Au
sein de la méthode OnUpdateModel() vous pouvez mettre à jour la propriété
Visual3DModel de la classe Visual3D. A noter que cette méthode ne possède
aucune implémentation par défaut dans la classe UIElement3D.
On définit une propriété de dépendance Modele pour UICylindre et on affecte à
cette propriété un GeometryModel3D intitulé mon_modele. Comme à l’habitude,
cet objet GeometryModel3D a sa propriété Geometry qui reçoit une géométrie
(celle d’un cylindre comme on l’a déjà vu UIGeoCylindre), et il a sa propriété
Material qui reçoit une texture de type Material.
On a vu que le cylindre avait une géométrie nécessitant un rayon interne,
un rayon externe, une hauteur, un nombre de divisions et un centre de
positionnement.
geometry.RayonExterne = RayonExterne;
geometry.Hauteur = Hauteur;
geometry.DivisionTheta = DivisionTheta;
geometry.Centre = Centre;
mon_modele.Geometry = geometry.Maillage;
mon_modele.Material = Material;
}
...}
CHAPITRE 7 □ L’interactivité des modèles 3D 215
Figure 7-1
216 La programmation graphique 3D de WPF 4
Il faut donc expliciter toutes les propriétés de dépendance de UICylindre, et
les relier à la géométrie UIGeoCylindre qui réalise effectivement le cylindre.
La démarche qui consiste à ajouter une propriété de dépendance s’effectue
toujours de la même façon quelque soit le type de la propriété de dépendance
à définir.
Prenons le cas de la création de la propriété de dépendance RayonInternePropriete,
qui gère le réglage du rayon interne lors de la construction du cylindre.
On commence par ajouter une propriété publique statique, en lecture seule
(readonly), de type DependencyProperty, nommée RayonInternePropriete. Sa
déclaration passe par l’emploi de la méthode statique DependencyProperty.
Register(..) qui reçoit en paramètre:
• une chaîne qui indique le nom de la propriété de dépendance à inscrire (ici
RayonInterne).
• un type de propriété correspondant à cette propriété (ici un type double).
• le type du propriétaire qui inscrit la propriété de dépendance (ici c’est
UICylindre).
• une instance de PropertyMetadata qui représente les métadonnées de
propriété pour la propriété de dépendance (ici on initialise le rayon
interne de type double à 0.0, et on passe le nom de la méthode statique,
ProprieteModifiee, qui modifie la propriété de dépendance).
Une fois la propriété de dépendance déclarée, on déclare les accesseurs, get et
set, pour lire et écrire la propriété RayonInterne.
Cette démarche doit être effectuée pour toutes les propriétés de dépendance
à déclarer, en faisant attention au type de la propriété et à son initialisation.
Par exemple pour la propriété Centre, il s’agit d’un type Point3D avec une
initialisation par new Point3D(0,0,0).
Pour la propriété DivisionTheta, qui est de type int, l’initialisation sera faite par
0 (et non 0.0 qui est un type double).
2 - Combinaison de modèles
Model = group;
}
...
}
Le cylindre et les deux tores ont des textures différentes. Deux propriétés de
dépendance sont déclarées pour pouvoir appliquer deux textures différentes.
3 - Les manipulations 3D
En informatique, un TrackBall est une boule insérée dans un clavier, que l’on
manipule avec les doigts et qui fait office de souris. Dans la modélisation 3D,
reproduire les déplacements de la souris d’un TrackBall avec une incidence sur
le visuel de la zone de rendu, est incontestablement un plus, et cela apporte
un niveau d’intuitivité supplémentaire. Nous allons implémenter ce TrackBall
pour le Viewport3D.
Le TrackBall transforme les mouvements 2D de la souris sur la zone de rendu
en mouvements 3D par des rotations. Cela est possible en projetant la position
de la souris sur une sphère imaginaire positionnée derrière le Viewport3D.
Comme le montre la figure 7-4, quand la souris se déplace, la caméra (ou la
scène) tourne en visant le même point sur la sphère positionnée derrière le
pointeur de la souris. Quand la souris bouge horizontalement, une rotation
autour de l’axe Y est nécessaire pour viser le même point. Il en est de même
verticalement par une rotation autour de l’axe X. Le TrackBall fournit une
méthode intuitive par laquelle un modèle 3D peut être manipulé dans toutes
les directions en appliquant une combinaison de rotations.
Pour mettre en place l’implémentation du TrackBall, il faut ajouter
Copyright 2011 Patrice REY
modèle 3D
TrackBall
Viewport3D
TrackBall
modèle 3D
axe Y
orientation
originale
nouvelle
orientation
232 La programmation graphique 3D de WPF 4
la méthode GetPosition(..). Puis on démarre la capture de la souris par cet
UIElement par la méthode UIElement.CaptureMouse().
Le bas de la figure 7-3 montre des copies d’écran de l’effet obtenu lors des
transformations de rotation.
pts[i, 0] = ObtenirPosition(m_rayon_ext,
i * 360 / (DivisionTheta - 1), h);
pts[i, 1] = ObtenirPosition(m_rayon_ext,
i * 360 / (DivisionTheta - 1), -h);
pts[i, 2] = ObtenirPosition(m_rayon_int,
i * 360 / (DivisionTheta - 1), -h);
pts[i, 3] = ObtenirPosition(m_rayon_int,
i * 360 / (DivisionTheta - 1), h);
CHAPITRE 7 □ L’interactivité des modèles 3D 235
}
int numero_indice = 0;
for (int i = 0; i < DivisionTheta - 1; i++) {
DessinerFacette(pts[i, 0], pts[i + 1, 0], pts[i + 1, 3],
pts[i, 3], mesh, numero_indice);
numero_indice += 4;
}
//
mesh.Freeze();
return mesh;
}
L’affectation d’une texture différente peut être alors réalisée en créant quatre
propriétés de dépendance MaterialDessus, MaterialDessous, MaterialExterne et
Copyright 2011 Patrice REY
MaterialInterne.
En XAML, il ne reste plus qu’à expliciter les textures des quatre faces par des
objets DiffuseMaterial dont leurs propriétés Brush sont des objets ImageBrush
(<obj:UICylindreV2.MaterialDessus>, etc.).
Les copies d’écran sur la figure 7-3 montrent bien que le cylindre en forme
d’anneau possède quatre faces texturées différemment.
4 - Projection 3D d’éléments 2D
implicite en XAML).
Pour réaliser la face avant du cube, on commence par définir la géométrie
de type MeshGeometry3D du Viewport2DVisual3D, avec les propriétés Positions,
TriangleIndices et TextureCoordinates.
<Viewport2DVisual3D.Geometry>
<MeshGeometry3D
Positions='-1 1 1,-1 -1 1,1 -1 1,1 1 1'
TriangleIndices='0,1,2 2,3,0'
TextureCoordinates='0 0,0 1,1 1,1 0' />
</Viewport2DVisual3D.Geometry>
...
</Viewport2DVisual3D>
Viewport2DVisual3D.IsVisualHostMaterial='true'
Brush='White' />
</Viewport2DVisual3D.Material>
...
</Viewport2DVisual3D>
Les surfaces 3D
D ans ce chapitre, vous allez voir comment générer des surfaces 3D diverses
et variées. Les surfaces jouent un rôle important dans de nombreuses
applications graphiques sur ordinateur, et notamment dans la visualisation
de données en 3D, dans le domaine de la réalité virtuelle et dans le domaine
des jeux.
Ce chapitre détaille différentes techniques de création de surfaces complexes,
incluant les surfaces paramétriques et extrudées.
La figure 8-2 visualise la première surface, intitulée surface1, qui va être générée.
Grâce au fonctionnement de la classe Surface, la réalisation d’une surface 3D
se réduit à la méthode GenererSurface01(..), en lui passant en paramètre les
chemins qui pointent vers les images des textures.
CHAPITRE 8 □ Les surfaces 3D 247
Figure 8-2
Un objet Surface est instancié en lui fixant ses propriétés voulues. On lui
applique la méthode DefinirLesTextures() pour fixer les textures issues des
images pointées. On lui applique la méthode CreerMaillageSurface(..) en lui
passant en paramètre la fonction CalculSurface01(..) qui retourne un Point3D.
Cette méthode CalculSurface01(..) reçoit en paramètre une coordonnée x et
une coordonnée z, puis calcule la coordonnée y, et retourne un point instancié
de type Point3D.
//surface 1
private void GenererSurface01(string chem_tex_dessus, string chem_
tex_dessous) {
Surface surface = new Surface() {
Centre = new Point3D(0, 0, 0),
Xmin = -8, Xmax = +8,
Ymin = -1, Ymax = +1,
Zmin = -8, Zmax = +8,
DivX = 30, DivZ = 30,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface01);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface01(double x, double z) {
double r = Math.Sqrt(x * x + z * z) + 0.00001;
248 La programmation graphique 3D de WPF 4
double y = Math.Sin(r) / r;
return new Point3D(x, y, z);
}
Les figures 8-3 et 8-4 montrent les équations des deux autres surfaces générées,
en utilisant exactement le même principe (instanciation d’une surface avec ses
propriétés voulues, puis application des textures, enfin calcul du maillage en
fonction d’une méthode retournant le résultat de l’équation).
Figure 8-3
Figure 8-4
Les surfaces paramétrées sont des surfaces dont leur réalisation consiste à
effectuer une rotation d’une courbe plane. Elles sont définies par une équation
paramétrique. Ce qui veut dire que pour un point avec une coordonnée x
et une coordonnée z, on aura plusieurs coordonnées y (contrairement au
paragraphe précédent).
L’UserControl ExSurfaceParametrique.xaml, dans le dossier chapitre08, visualise
la création de 7 surfaces 3D paramétrées à partir d’équations paramétriques.
Ces équations génèrent des surfaces paramétrées par rotation d’une courbe
plane (elles sont très souvent utilisées dans la visualisation de données). Les
figures 8-5 et 8-6 montrent les surfaces obtenues.
Pour réaliser une surface paramétrée, nous devons créer une classe
SurfaceParametrique, qui est identique à la classe Surface créée précédemment,
mais avec quatre propriétés supplémentaires (Umin, Umax, Vmin et Vmax). A
noter que le délégué FonctionCalcul reçoit en paramètre u et v, de type double,
et non pas x et z comme précédemment. Tous les champs privés de travail
sont dotés de leurs accesseurs respectifs.
La surface numéro 1 réalisée est une hélicoïde. Sa forme filaire et son équation
paramétrique sont visualisées sur la figure 8-7. Elle a une forme d’hélice. Sa
réalisation consiste à faire tourner un segment autour d’une droite verticale
tout en l’élevant.
Figure 8-7
équation paramétrique :
x = u * cos(v)
y=v
z = u * sin(v)
254 La programmation graphique 3D de WPF 4
Dans l’exemple, on fait varier u entre 0 et 1, et v entre -3π et +3π. Le code de
programmation de l’hélicoïde est le suivant:
//surface 1 helicoid
private void GenererSurface01(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceParametrique surface = new SurfaceParametrique() {
Centre = new Point3D(0, 0, 0),
Xmin = -3, Xmax = +3,
Ymin = -3 * Math.PI, Ymax = +3 * Math.PI,
Zmin = -3, Zmax = +3,
DivU = 10, DivV = 100,
Umin = 0, Umax = 1,
Vmin = -3 * Math.PI, Vmax = 3 * Math.PI,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface01);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface01(double u, double v) {
double x = u * Math.Cos(v);
double z = u * Math.Sin(v);
double y = v;
return new Point3D(x, y, z);
}
La surface numéro 2 réalisée est une sphère. Sa forme filaire et son équation
paramétrique sont visualisées sur la figure 8-8. Elle a une forme de sphère
comme on a déjà pu le voir mais cette fois avec une équation paramétrique.
Sa réalisation consiste à faire tourner un cercle autour d’une droite verticale.
Dans l’exemple, on fait varier u entre 0 et +2π, et v entre -0.5π et +0.5π. Le
code de programmation de la sphère avec une équation paramétrique est le
Copyright 2011 Patrice REY
suivant:
//surface 2 sphere
private void GenererSurface02(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceParametrique surface = new SurfaceParametrique() {
Centre = new Point3D(0, 0, 0),
CHAPITRE 8 □ Les surfaces 3D 255
Figure 8-8
équation paramétrique :
x = cos(u) * cos(v)
y = sin(v)
z = sin(u) * cos(v)
équation paramétrique :
x = cos(u) * (1 + a * cos(v))
y = a * sin(v)
z = sin(u) * (1 + a * cos(v))
//surface 3 tore
private void GenererSurface03(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceParametrique surface = new SurfaceParametrique() {
Centre = new Point3D(0, 0, 0),
Xmin = -3, Xmax = +3,
Ymin = -8, Ymax = +8,
Zmin = -3, Zmax = +3,
DivU = 50, DivV = 20,
Umin = 0, Umax = 2 * Math.PI,
Vmin = 0, Vmax = 2 * Math.PI,
Copyright 2011 Patrice REY
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface03);
x_surfaces.Children.Add(surface.Modele);
}
//
CHAPITRE 8 □ Les surfaces 3D 257
private Point3D CalculYSurface03(double u, double v) {
double x = (1 + 0.3 * Math.Cos(v)) * Math.Cos(u);
double z = (1 + 0.3 * Math.Cos(v)) * Math.Sin(u);
double y = 0.3 * Math.Sin(v);
return new Point3D(x, y, z);
}
équation paramétrique :
x = a * cos(u) * cosh(v)
y = b * sinh(v)
z = b * sin(u) * cosh(v)
//surface 4 hyperboloide
private void GenererSurface04(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceParametrique surface = new SurfaceParametrique() {
Centre = new Point3D(0, 0, 0),
Xmin = -3, Xmax = +3,
Ymin = -8, Ymax = +8,
Zmin = -3, Zmax = +3,
258 La programmation graphique 3D de WPF 4
DivU = 20, DivV = 10,
Umin = 0, Umax = 2 * Math.PI,
Vmin = -1, Vmax = 1,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface04);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface04(double u, double v) {
double x = 0.3 * Math.Cos(u) * Math.Cosh(v);
double z = 0.5 * Math.Sin(u) * Math.Cosh(v);
double y = 0.5 * Math.Sinh(v);
return new Point3D(x, y, z);
}
équation paramétrique :
x = a * cosh(u) * v
y=v*v
z = b * sinh(u) * v
Copyright 2011 Patrice REY
équation paramétrique :
x = a * cos(u) * v
y=b*v
z = c * sin(u) * v
équation paramétrique :
x = a * cos(u)
y=v
z = b * sin(u)
La figure 8-15 montre la surface extrudée numéro 1 qui est générée. A gauche,
on voit le maillage filaire de la surface, et à droite on voit le profil qui est
déplacé le long du chemin pour générer la surface.
CHAPITRE 8 □ Les surfaces 3D 265
Figure 8-15
la forme extrudée
le chemin
le profil
//surface extrudee 1
private void GenererSurface01(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceExtrudee surf_extrudee = new SurfaceExtrudee() {
Centre=new Point3D (0,5,0),
Xmin = -3, Xmax = 3,
Ymin = 5, Ymax = 20,
Zmin = -3, Zmax = 5,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous
};
for (int i = 0; i < 10; i++) {
double angle = i * Math.PI / 16 + 3 * Math.PI / 2;
surf_extrudee.ListePointsProfil.Add(
new Point3D(Math.Cos(angle), 0, Math.Sin(angle)));
266 La programmation graphique 3D de WPF 4
}
for (int i = 0; i < 33; i++) {
surf_extrudee.ListePointsTrace.Add(
new Point3D(Math.Cos(i * Math.PI / 12), i * Math.PI / 12, 0));
}
surf_extrudee.DefinirLesTextures();
surf_extrudee.CreerMaillageSurface();
x_surfaces.Children.Add(surf_extrudee.Modele);
}
la forme extrudée
//surface extrudee 2
private void GenererSurface02(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceExtrudee surf_extrudee = new SurfaceExtrudee() {
Centre = new Point3D(0, 10, 0),
Xmin = -3, Xmax = 3,
Ymin = 5, Ymax = 30,
Zmin = -3, Zmax = 3,
Copyright 2011 Patrice REY
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous
};
for (int i = 0; i < 17; i++) {
double angle = i * Math.PI / 8;
surf_extrudee.ListePointsProfil.Add(
new Point3D(1 + 0.3*Math.Cos(angle),
0, 0.3 * Math.Sin(angle)));
CHAPITRE 8 □ Les surfaces 3D 267
}
for (int i = 0; i < 45; i++) {
double angle = i * Math.PI / 16;
surf_extrudee.ListePointsTrace.Add(
new Point3D(1.3 *Math.Cos(angle),
angle, 1.3 * Math.Sin(angle)));
}
surf_extrudee.DefinirLesTextures();
surf_extrudee.CreerMaillageSurface();
x_surfaces.Children.Add(surf_extrudee.Modele);
}
Une surface de révolution est obtenue par rotation d’une courbe plane autour
d’un axe dit de révolution. Généralement il s’agit de l’axe Y comme support
d’axe de révolution.
L’UserControl ExSurfaceRevolution.xaml, dans le dossier chapitre08, visualise
une surface de révolution générée par l’intermédiaire d’une courbe plane qui
tourne autour de l’axe Y. La figure 8-17 montre le résultat de la courbe obtenue
sous divers angles.
La démarche consiste à calculer les points d’une courbe dans un intervalle
donné et à procéder à leur rotation autour de l’axe Y en fonction d’un pas
défini à l’avance (un nombre de divisions comme on a déjà pu le voir dans
d’autres chapitres).
On définit une classe SurfaceRevolution pour générer une surface de révolution.
Les champs privés m_x_min, m_x_max, m_y_min, m_y_max, m_z_min et m_z_
max permettent d’encadrer les valeurs dans des étendues spécifiques. Le
champ m_div_theta détermine le nombre de facettes qui seront générées pour
un tour complet autour de l’axe Y. La liste m_liste_pts_profil contient tous les
points de la surface en vue de leur édition pour l’affichage.
Les champs privés ont tous leurs accesseurs respectifs pour procéder à
l’instanciation d’un objet SurfaceRevolution.
//proprietes publiques
public List<Point3D> ListePointsProfil {
get { return m_liste_pts_profil; }
set { m_liste_pts_profil = value; }
}
public double Xmin {
get { return m_x_min; }
set { m_x_min = value; }
}
public double Xmax {
get { return m_x_max; }
set { m_x_max = value; }
}
public double Ymin {
get { return m_y_min; }
set { m_y_min = value; }
}
public double Ymax {
get { return m_y_max; }
set { m_y_max = value; }
}
public double Zmin {
get { return m_z_min; }
set { m_z_min = value; }
}
public double Zmax {
get { return m_z_max; }
set { m_z_max = value; }
270 La programmation graphique 3D de WPF 4
}
public int DivTheta {
get { return m_div_theta; }
set { m_div_theta = value; }
}
public Point3D Centre {
get { return m_centre; }
set { m_centre = value; }
}
...
}
int numero_indice = 0;
Point3D[] p = new Point3D[4];
for (int i = 0; i < m_div_theta - 1; i++) {
for (int j = 0; j < m_liste_pts_profil.Count - 1; j++) {
p[0] = pts[i, j];
p[1] = pts[i + 1, j];
p[2] = pts[i + 1, j + 1];
CHAPITRE 8 □ Les surfaces 3D 271
p[3] = pts[i, j + 1];
DessinerFacette(p[0], p[1], p[2], p[3], m_mesh, numero_indice);
numero_indice += 4;
}
}
}
axe de
révolution Y
profil de la
surface de courbe
révolution
générée
//surface revolution 1
272 La programmation graphique 3D de WPF 4
private void GenererSurface01(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceRevolution surf_revol = new SurfaceRevolution() {
Centre = new Point3D(0, 5, 0),
Xmin = -3, Xmax = 3,
Ymin = 5, Ymax = 15,
Zmin = -3, Zmax = 3,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous
};
for (int i = 0; i < 33; i++) {
double y = i * Math.PI / 12;
double siny = Math.Sin(y);
surf_revol.ListePointsProfil.Add(
new Point3D(0.2 + siny * siny, y, 0));
}
surf_revol.DefinirLesTextures();
surf_revol.CreerMaillageSurface();
x_surfaces.Children.Add(surf_revol.Modele);
}
A
cylindre 155
cylindre elliptique 260
D
AffineTransform3D 79
AmbientColor 108
AmbientLight 108, 112
Angle 55
Append 51
DependencyProperty.Register 216
Axis 55
Determinant 44
AxisAngleRotation3D 89, 123
DiffuseMaterial 106, 108
axonométriques 63
Direction 113
direction du haut 66
B
direction du regard 66
DirectionLight 112
dodécaèdre 206
DrawingBrush 109
E
BackMaterial 102
Brush 107, 108
C
Camera 96
éclairage de la scène 111
effet miroir 31
EmissiveMaterial 108
Equals 20
caméra 64
F
CenterX 81, 86
CenterY 81, 86
CenterZ 81, 86
centre de projection 59
Children 96
cisaillement 35
facette triangulaire 117
ClipToBounds 117
FielOfView 115
Color 108
cône 160
G
cône elliptique 259
ConstantAttenuation 112
ContainerUIElement3D 99
coordonnées euclidiennes 28
coordonnées homogènes 28, 38
288 Index
géométrie 171 Look Direction 66
Geometry 102 LookDirection 114
GeometryModel3D 99, 101
H M
m11 39
HasInverse 44 m12 39
hélicoïde 253 m13 39
HitTestResult 228 m14 39
hyperboloïde 257 m21 39
m22 39
I
m23 39
m24 39
m31 39
m32 39
m33 39
m34 39
icosaèdre 201 main droite 11
ImageBrush 129 Material 102, 107
InnerConeAngle 113 MaterialGroup 108, 109
InvalidateModel 214 matrice de transformation 29
Inverse 81, 87 matrice inversée 47
inversible 46 Matrix 90
Invert 51 Matrix3D 44
IsAffine 44, 81 Matrix3D.Identity 44
IsIdentity 44, 55 Matrix3D.Multiply 51
IsNormalized 55 MatrixCamera 68, 113
isométrique 63 MatrixTransform3D 79, 90
IsVisualHostMaterial 240 MeshGeometry3D 102
Model3D 97
Model3DGroup 99, 101
L
modélisation basique 117
Copyright 2011 Patrice REY
ModelUIElement3D 95
ModelVisual3D 95, 97
N
Length 13
LengthSquared 13
Light 111
LinearAttenuation 112
Index 289
Normalize 26 PointLight 112
Normals 103 PointLightBase 112
Positions 103
O
Prepend 51
projecteurs 59
projection 59
ProjectionCamera 113
ProjectionMatrix 68
projection parallèle 63, 72
obliques 63 projection perspective 61
Offset 19, 43
Q
OffsetX 44
OffsetY 44
OffsetZ 44
OnUpdateModel 214
OrthographicCamera 113, 115
orthonormales 63
OuterConeAngle 113 QuadraticAttenuation 113
Quaternion 55
P
Quaternion.Identity 55
QuaternionRotation3D 89
paraboloïde 258
parallèle 59
perspective 59
PerspectiveCamera 113
R
Range 112
plan de projection 65 RayMeshGeometry3DHitTestResult 228
Point3D 11 réflexion 31
Point3D.Add 15 réflexion spéculaire 110
Point3DCollection 12 rendu 59
Point3D.Parse 21 Rotate 51
Point3D.Substract 16 RotateAt 51
Point4D 42 RotatePrepend 57
Point4D.Add 43 RotateTransform3D 80, 87
Point4D.Equals 43 Rotation 86
Point4D.Multiply 43
S
Point4D.Parse 43
Point4D.Substract 43
point de fuite 61
point de fuite axial 61
PointHit 228
290 Index
U
Scale 51
ScaleAt 51
ScalePrepend 53
ScaleTransform3D 79, 81
ScaleX 81
ScaleY 81 UIElement3D 99, 213
ScaleZ 81 Up Direction 66
SpecularMaterial 108 UpDirection 114
SpecularPower 110
V
sphère 149
SpotLight 112
surface 3D 243
surface de révolution 267
surface extrudée 262
surface lumineuse 109
surface mate 108 Value 81
surfaces paramétrées 250 Vector3D 11
Vector3D.Add 21
T
Vector3D.AngleBetween 26
Vector3D.Multiply 23
ViewMatrix 68
Viewport2DVisual3D 238
Viewport3D 95, 96
Visual3D 96, 238
test d’atteinte 225 VisualBrush 129
TextureCoordinates 103 VisualHit 228
tore 166 VisualTreeHelper 227
TrackBall 225, 230 volume d’observation 64
Transform 51, 69
W
Transform3D 79
Transform3DCollection 79
Transform3DGroup 79
transformations 3D 79
transformations géométriques 28
TransformGroup3D 94
Translate 51 W 71
Width 115
Copyright 2011 Patrice REY
TranslatePrepend 51
TranslateTransform3D 80, 84
TriangleIndices 103
Guide
de
programmation
La programmation graphique 2D de WPF 4
(ISBN 978-2-8106-1199-7)
http://www.decitre.fr/livres/La-programmation-graphique-2D-de-WPF-4.
aspx/9782810611997
Initiation au jeu 2D avec Silverlight 4
(ISBN 978-2-8106-1168-3)
chapitre 1 : la vectorisation
chapitre 2 : création du projet
chapitre 3 : boucle de jeu et rendu
chapitre 4 : le déplacement des images
chapitre 5 : la détection des collisions
chapitre 6 : le score et la fin de partie
chapitre 7 : un écran d’accueil
chapitre 8 : la pause et l’arrêt de jeu
chapitre 9 : sonoriser le jeu
chapitre 10 : téléchargement du jeu
http://www.decitre.fr/livres/Initiation-au-jeu-avec-silverlight-4.aspx/9782810611683
Les structures de données
illustrées avec WPF et C#4
(ISBN 978-2-8106-1387-8)
chapitre 1 : la récursivité
chapitre 2 : les piles
chapitre 3 : les files
chapitre 4 : les listes chaînées
chapitre 5 : les arbres
chapitre 6 : les méthodes de tri
chapitre 7 : les recherches
chapitre 8 : les tables
http://www.decitre.fr/livres/Les-structures-de-donnees.aspx/9782810613878
La programmation graphique 3D de WPF 4
ISBN : 978-2-8106-2086-9
Éditeur :
Books on Demand GmbH, 41 €
12/14 rond point des
Champs Élysées,
75008 Paris,
France