Vous êtes sur la page 1sur 306

Programmation C#

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.

Titres déjà parus


Initiation au jeu 2D avec Silverlight 4
(ISBN : 978-2-8106-1168-3)
Les structures de données illustrées avec WPF et C#4
(ISBN : 978-2-8106-1387-8)
La programmation graphique 2D de WPF 4
(ISBN : 978-2-8106-1199-7)

É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 1 - Les vecteurs et les matrices en 3D


1. Les structures Point3D et Vector3D ................................................... 11
1.1 - Définition des structures 3D ....................................................... 11
1.2 - Utilisation des structures 3D ...................................................... 14
2. Coordonnées euclidiennes et coordonnées homogènes .................. 28
2.1 - Transformations en coordonnées euclidiennes ........................ 28
2.2 - Définition des coordonnées homogènes ................................. 38
2.3 - Transformations en coordonnées homogènes ........................ 38
2.4 - La structure Point4D ............................................................... 42
3. La structure Matrix3D ....................................................................... 44
3.1 - Opérations sur la matrice 3D ................................................... 46
3.2 - Transformations sur la matrice 3D ......................................... 51

Chapitre 2 - Les projections


1. Les types de projection ..................................................................... 59
1.1 - Les projections perspectives ................................................... 61
1.2 - Les projections parallèles ......................................................... 63
2. La matrice de la projection perspective ........................................... 64
3. La matrice de la projection parallèle ............................................... 72

Chapitre 3 - Les transformations 3D


1. Les classes des transformations 3D .................................................... 79
2. La transformation ScaleTransform3D .............................................. 80
4 Table des matières

3. La transformation TranslateTransform3D ........................................ 84


4. La transformation RotateTransform3D ............................................ 86
5. La transformation MatrixTransform3D ........................................... 90
6. Combinaison de transformations ..................................................... 94

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

Chapitre 5 - Les modèles 3D basiques


1. La modélisation basique ................................................................... 117
1.1 - La facette triangulaire ................................................................. 117
1.2 - La facette rectangulaire .............................................................. 126
2. Le cube .............................................................................................. 130
2.1 - Un cube avec une couleur uniforme ............................................ 131
2.2 - Un cube avec des faces colorées .................................................. 136
2.3 - Les axes de coordonnées X, Y et Z ............................................ 144
3. La sphère ........................................................................................... 149
4. Le cylindre ........................................................................................ 155
Table des matières 5

5. Le cône ............................................................................................... 160


6. Le tore ............................................................................................... 166

Chapitre 6 - Les géométries 3D personnalisées


1. La géométrie personnalisée du cube ................................................ 171
2. La géométrie personnalisée de la sphère ......................................... 178
3. La géométrie personnalisée du cylindre .......................................... 184
4. La géométrie personnalisée du cône ............................................... 190
5. La géométrie personnalisée du tore ................................................ 196
6. La géométrie personnalisée de l’icosaèdre ...................................... 201
7. La géométrie personnalisée du dodécaèdre .................................... 206

Chapitre 7 - L’interactivité des modèles 3D


1. Hériter de UIElement3D .................................................................... 213
2. Combinaison de modèles ................................................................. 221
3. Les manipulations 3D ....................................................................... 225
3.1 - Implémenter le test d’atteinte ..................................................... 225
3.2 - Implémenter un TrackBall .......................................................... 230
3.3 - Objet 3D avec plusieurs textures ................................................ 233
4. Projection 3D d’éléments 2D ............................................................. 238

Chapitre 8 - Les surfaces 3D


1. Générer une surface 3D .................................................................... 243
2. Les surfaces paramétrées ................................................................... 250
3. Les surfaces extrudées ...................................................................... 262
4. Les surfaces de révolution ............................................................... 267

Annexe .................................................................................................... 275

Index ........................................................................................................ 287


Avant-propos

WPF (Windows Presentation Foundation) est une technologie de développement


d’application riche (Rich Desktop Application) pour la plateforme Windows. La
version actuelle de WPF est la version 4, celle qui correspond à .NET 4.0, qui
est sortie en avril 2010. Depuis 2006, date de la première sortie de WPF, cette
technologie est devenue une technologie majeure du développement pour
Windows en code managé.
Les innovations de WPF, à la fois sur le plan architectural et sur le plan
fonctionnel, sont très importantes dans sa version actuelle. WPF constitue
une synthèse et une unification des différentes branches de l’informatique
graphique (graphismes vectoriels 2D et 3D, saisie, animation, typographie et
multimédia).
A noter que WPF ne cible pas la réalisation des jeux vidéo évolués puisque
Microsoft fournit des technologies évoluées et adaptées qui sont XNA en
code managé et DirectX en code natif.
WPF est un framework managé qui exploite la puissance de Direct3D pour
l’affichage. Cette technologie de développement d’interface utilisateur permet
de moderniser et unifier les techniques de développement visuel, d’exploiter
la puissance graphique des machines modernes, d’intégrer l’infographie dans
le processus de développement, et de simplifier et sécuriser le déploiement
des applications.
En s’appuyant sur Direct3D, WPF exploite toute la puissance du GPU (Graphic
Processing Unit ou le processeur graphique), ce qui permet de libérer le CPU
(Central Processing Unit) et d’envisager des applications graphiques de plus
en plus riches.
WPF sépare dans des couches distinctes la programmation des fonctionnalités
(au moyen d’un langage .NET tel que le C#) de la présentation visuelle par
l’utilisation du langage XAML (eXtensible Application Markup Language).
8 Avant-propos

Les logiciels requis pour le développement

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

Les différents chapitres permettent d’apprendre et de mettre en pratique les


notions et concepts essentiels de la programmation graphique 3D de WPF. Les
chapitres abordent les points suivants:
• le chapitre 1 permet de revoir et d’appliquer tout ce qui concerne les
vecteurs et l’algèbre matricielle; les vecteurs et les matrices 3D représentent
les bases mathématiques de toute conception 3D.
• le chapitre 2 traite des différentes projections utilisées dans WPF
(perspective, parallèle) avec l’utilisation de leur matrice respective.
• le chapitre 3 permet d’apprendre l’utilisation de toutes les transformations
3D implémentées dans WPF.
• le chapitre 4 permet d’apprendre à concevoir une scène 3D avec tout ce
qui est nécessaire pour la visualisation (zone de rendu, caméra, lumière,
objet 3D, etc.).
• le chapitre 5 traite de la conception des objets 3D basiques d’un point de
vue de la modélisation; c’est un chapitre essentiel concernant la génération
des modèles 3D.
• le chapitre 6 permet d’apprendre à modéliser des objets 3D personnalisés
en vue de leur intégration dans une scène 3D.
• le chapitre 7 traite de l’interactivité des modèles 3D, avec notamment
les implémentations qui permettent à la souris de sélectionner un objet
3D par un clic (test d’atteinte) ainsi qu’au déplacement des objets 3D par
l’utilisation de la souris (implémentation du TrackBall).
• le chapitre 8 traite des différentes surfaces 3D qui peuvent être générées
comme les surfaces paramétrées, les surfaces extrudées et les surfaces de
révolution.
Toutes les notions abordées dans ce livre font l’objet d’une utilisation directe
et pratique que vous trouverez dans le code source en téléchargement.

Les liens de téléchargement

Tout le code source de cet ouvrage pourra être téléchargé gratuitement à


l’adresse web suivante:
http://www.reypatrice.fr/programmation_3d_wpf.php.
10 Avant-propos
Vous trouverez un dossier pour chacun des chapitres. Chaque dossier contient
tous les éléments nécessaires de programmation qui sont expliqués dans le
livre. De cette façon, vous serez capable de suivre et de programmer assez
rapidement et assez facilement.
C’est une démarche volontaire, dans un but pédagogique, pour progresser
rapidement. Le but de ce livre est d’être capable d’acquérir dans un temps
raisonnable l’acquisition des notions et concepts de la programmation
graphique 3D de WPF.

Bonne lecture à tous.


CHAPITRE 1

Les vecteurs et les matrices en 3D

D ans WPF, l’espace de noms System.Windows.Media.Media3D (assembly


PresentationCore.dll) contient toutes les classes et toutes les structures qui
supportent la représentation 3D. Les fonctionnalités 3D permettent aux
développeurs de dessiner, de transformer et d’animer des graphiques 3D,
à la fois en XAML et en code procédural. Les développeurs peuvent aussi
combiner de la 2D et des graphiques 3D pour créer des contrôles riches, pour
fournir des illustrations complexes de données et pour améliorer l’expérience
utilisateur de l’interface d’une application.
Dans ce chapitre, nous allons voir la représentation mathématique de la 3D
avec les points, les vecteurs et les matrices.

1 - Les structures Point3D et Vector3D

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.

1.1 - Définition des structures 3D

Dans un système à 3 dimensions, un point est représenté par ses coordonnées


selon les 3 axes X, Y et Z. Dans le repère 3D utilisé par WPF, depuis l’origine O
du repère, l’axe des abscisses X positives part vers la droite, l’axe des ordonnées
Y positives part vers le haut, et l’axe des coordonnées Z positives part vers
l’extérieur et devant. Ces 3 axes peuvent être représentés au moyen des doigts
d’une main: le pouce représente l’axe des X, l’index représente l’axe des Y et
le majeur représente l’axe des Z. L’axe des X est généralement dirigé vers la
droite et l’axe des Y est généralement dirigé vers le haut. Pour la direction de
l’axe des Z, WPF utilise le système «main droite» dans lequel l’axe des Z est
dirigé vers l’avant (il sort de l’écran). La figure 1-1 visualise ces 3 axes.
12 La programmation graphique 3D de WPF 4
Figure 1-1 Y

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

Copyright 2011 Patrice REY

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

1.2 - Utilisation des structures 3D

L’UserControl UtilisationPoint3dVector3D.xaml, dans le dossier chapitre01,


visualise l’utilisation des méthodes pour les structures Point3D et Vector3D
(figure 1-4).

Figure 1-4

La case à cocher 01 utilise l’opérateur surchargé +, de la structure Point3D,


Copyright 2011 Patrice REY

pour additionner un Point3D à un Vector3D. Par exemple nous additionnons le


point A(2,1,3) au vecteur V(7,3,1). Le résultat donne un point B(9,4,4) de type
Point3D. La figure 1-5 visualise la localisation des points et du vecteur.

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

La case à cocher 02 utilise la méthode statique Point3D.Add(..) pour effectuer le


même calcul, en prenant en paramètre un point de type Point3D et un vecteur
de type Vector3D.
16 La programmation graphique 3D de WPF 4
case «x_rbtn_2»: {
Point3D point_1 = new Point3D(2, 1, 3);
Vector3D vecteur_1 = new Vector3D(7, 3, 1);
Point3D point_resultat = new Point3D();
point_resultat = Point3D.Add(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;
}

Les cases à cocher 03 et 04 permettent d’effectuer la soustraction d’un vecteur


de type Vector3D à un point de type Point3D. La case à cocher 03 utilise
l’opérateur surchargé - de la structure Point3D. La case à cocher 04 utilise la
méthode statique Point3D.Substract(..), en prenant en paramètre un point de
type Point3D et un vecteur de type Vector3D. La figure 1-6 visualise le vecteur
V(7,3,1) que l’on retire au point A(9,4,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

Vector3D vecteur_1 = new Vector3D(7, 3, 1);


Point3D point_resultat = new Point3D();
point_resultat = Point3D.Subtract(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 = Point3D.Subtract(point_1, vecteur_1)» + RC;
v_resultat += «-> point_resultat : Point3D(2,1,3)» + RC;
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 17
v_resultat += point_resultat.ToString() + RC;
break;
}
Figure 1-6 Y

4
V(7,3,1)

A(9,4,4)
1
2 9
X

B(2,1,3)
3
4

Les cases à cocher 05 et 06 permettent de trouver le vecteur en fonction de


deux points. Le choix de la case à cocher 05 utilise l’opérateur surchargé - de
Point3D pour soustraire le point point_1 au point point_2. Le résultat est un
vecteur de type Vector3D, dont ses composantes représentent un déplacement,
donc c’est un vecteur orienté. Le choix de la case 06 utilise la méthode statique
Point3D.Substract(..), qui prend en paramètre deux points Point3D et retourne
une vecteur Vector3D. La figure 1-7 visualise le vecteur AB, orienté de A vers
B, lorsque l’on soustrait le point B au point A.
18 La programmation graphique 3D de WPF 4
case «x_rbtn_5»: {
Point3D point_1 = new Point3D(2,1,3);
Point3D point_2 = new Point3D(9,4,4);
Vector3D vecteur_resultat = new Vector3D();
vecteur_resultat = 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 = point_2 - point_1» + RC;
v_resultat += «-> vecteur_resultat : Point3D(7,3,1)» + RC;
v_resultat += vecteur_resultat.ToString() + RC;
vecteur_resultat = point_1 - point_2;
v_resultat += «point_1 : Point3D(2,1,3)» + RC;
v_resultat += «point_2 : Point3D(9,4,4)» + RC;

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;
}

La case à cocher 07 permet de déplacer un point par la méthode Offset(..). On


a vu que le fait d’ajouter un vecteur à un point, donnait un point résultant qui
correspondait au déplacement du point initial. La méthode Offset(..) effectue
la même chose. Elle reçoit en paramètre les coordonnées de translation selon
les trois axes.

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

v_resultat += «point_2 : Point3D(9,4,4)» + RC;


v_resultat += «-> sont_egaux = point_1.Equals(point_2)» + RC;
v_resultat += «-> sont_egaux = false» + RC;
v_resultat += sont_egaux.ToString() + RC;
break;
}

La case à cocher 11 permet de convertir une chaîne textuelle représentant


CHAPITRE 1 □ Les vecteurs et les matrices en 3D 21
les composantes d’une structure Point3D en une structure de type Point3D.
On utilise la méthode statique Point3D.Parse(..) qui reçoit en paramètre une
chaîne textuelle (point_1 = Point3D.Parse(«2,1,3»);).

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;
}

La case à cocher 12 permet de tester l’inégalité entre deux Point3D avec


l’opérateur surchargé != (bool sont_inegaux = (point_1 != point_2);).

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;
}

Les cases à cocher 13 et 14 permettent d’additionner deux vecteurs de type


Vector3D. La figure 1-8 visualise deux vecteurs, V1(1,3,5) et V2(3,-3,0). Le
vecteur résultant est V(4,0,5). La case à cocher 13 permet d’effectuer la somme
de vecteurs par l’opérateur surchargé + de Vector3D. La case à cocher 14 permet
d’effectuer la somme de vecteurs par la méthode statique Vector3D.Add(..).

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;
}

Les cases à cocher 17 et 18 permettent de multiplier un Vector3D par une


valeur. Le choix de la case 17 permet d’effectuer cette opération en multipliant
le vecteur par une valeur (vecteur_resultat = vecteur_1 * scalaire). Le choix de la
case 18 permet d’effectuer cette opération par la méthode statique Vector3D.
Multiply(..). Dans l’exemple, sur la figure 1-9, le vecteur V1(3,1,-2) est multiplié
par une valeur de 2, ce qui donne un vecteur V2(6,2,-4).

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;
}

La case à cocher 19 permet d’obtenir la longueur d’un vecteur (propriété


Length) et la case à cocher 20 permet d’obtenir le carré de la longueur (propriété
LengthSquared). La figure 1-10 montre un vecteur V(4,4,4). Sa longueur est de
6.92 et le carré de sa longueur est de 48. La longueur d’un vecteur (dit aussi sa
norme) est égale à la racine carrée de la somme de ses composantes au carré
(racine carrée de 42+42+42).

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;
}

La case à cocher 21 permet d’effectuer la normalisation d’un vecteur. La


normalisation consiste à trouver un vecteur normalisé ayant la même direction
que le vecteur d’origine, mais avec une longueur égale à 1. Cette normalisation
s’effectue par la méthode Normalize().

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;
}

La case à cocher 22 permet de trouver l’angle requis pour faire pivoter la


première structure Vector3D spécifiée dans la seconde structure Vector3D. La
figure 1-11 montre deux vecteurs, V1(4,0,0) et V2(0,0,4). Pour passer de la
Copyright 2011 Patrice REY

première structure à la deuxième structure, il faut un angle de 90 degrés.


La méthode statique Vector3D.AngleBetween(..) permet de trouver cet angle
(angle_entre = Vector3D.AngleBetween(vecteur_1, vecteur_2);).

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

2 - Coordonnées euclidiennes et coordonnées homogènes

Toute composition de rotation, de translation, de mise à l’échelle, de


cisaillement, est appelée une transformation affine. Soit deux points
P1(x1,y1,z1) et P2(x2,y2,z2) de l’espace 3D en coordonnées euclidiennes. Si
P2 est l’image de P1 par une transformation, alors cette transformation sera
dite «affine» si elle respecte la condition suivante:

La notation P2=M.P1+T veut dire que P2 est obtenu en multipliant P1 par la


matrice M puis en lui ajoutant la matrice T. La transformation affine a pour
particularité de conserver le parallélisme des droites mais de ne pas préserver
ni les longueurs, ni les angles.

2.1 - Transformations en coordonnées euclidiennes

Nous allons voir comment les transformations géométriques s’expriment en


coordonnées euclidiennes avec la translation, la mise à l’échelle, la rotation et
le cisaillement.
La translation consiste à déplacer un point d’une distance Tx suivant l’axe des
X, d’une distance Ty suivant l’axe des Y et d’une distance Tz suivant l’axe
des Z. Le passage du point P1(x1,y1,z1) au point P2(x2,y2,z2) se fait par la
transformation T tel que P2=P1+T.
Les coordonnées de P2(x2,y2,z2) sont obtenues à partir des coordonnées de
P1(x1,y1,z1) par la transformation de translation de la façon suivante:
Copyright 2011 Patrice REY
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 29
La figure 1-12 visualise le point P1(4,0,6) qui est transformé en P2(8,1,4) par
une translation T(Tx=4, Ty=1, Tz=-2).
Figure 1-12 Y

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:

x2 = Sx*x1 + 0*y1 + 0*z1


donc x2 = Sx*x1

y2 = 0*x1 + Sy*y1 + 0*z1


donc y2 = Sy*y1

z2 = 0*x1 + 0*y1 + Sz*z1


donc z2 = Sz*z1

Figure 1-13 Y

Copyright 2011 Patrice REY

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).

x2 = -1*x1 + 0*y1 + 0*z1


donc x2 = -x1

y2 = 0*x1 + 1*y1 + 0*z1


donc y2 = y1

z2 = 0*x1 + 0*y1 + 1*z1


donc 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).

x2 = 1*x1 + 0*y1 + 0*z1


donc x2 = x1

y2 = 0*x1 + (-1*y1) + 0*z1


donc y2 = -y1
32 La programmation graphique 3D de WPF 4

z2 = 0*x1 + 0*y1 + 1*z1


donc 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).

x2 = 1*x1 + 0*y1 + 0*z1


donc x2 = x1

y2 = 0*x1 + 1*y1 + 0*z1


donc y2 = y1

z2 = 0*x1 + 0*y1 + (-1*z1)


donc z2 = -z1

La matrice de rotation pour faire pivoter les points d’un angle α autour de
l’axe des X est la suivante:

Copyright 2011 Patrice REY

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 rotation:
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 33

x2 = 1*x1 + 0*y1 + 0*z1


donc x2 = x1

y2 = 0*x1 + cos(α)*y1 + (-sin(α)*z1)


donc y2 = y1*cos(α) - z1*sin(α)

z2 = 0*x1 + sin(α)*y1 + cos(α)*z1


donc z2 = y1*sin(α) + z1*cos(α)

Le calcul matriciel donne, pour un point P1(x1,y1,z1), un point P2(x2,y2,z2)


avec x2=x1, y2=y1*cos(α)-z1*sin(α) et z2=y1*sin(α)+z1*cos(α). La figure
1-14 visualise la rotation d’un segment avec α=-90°. Le point A1(0,0,2) est
transformé en A2(0,2,0) et le point C1(6,0,2) est transformé en C2(6,2,0).
Figure 1-14 Y

B2(0,5,0) D2(6,5,0)

-90° A2(0,2,0) C2(6,2,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:

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 rotation:

x2 = cos(α)*x1 + (-sin(α)*y1) + 0*z1


donc x2 = x1*cos(α) - y1*sin(α)

y2 = sin(α)*x1 + cos(α)*y1 + 0*z1


donc y2 = x1*sin(α) + y1*cos(α)

z2 = 0*x1 + 0*y1 + 1*z1


donc z2 = z1

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

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 rotation:
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 35

x2 = cos(α)*x1 + (-sin(α)*y1) + 0*z1


donc x2 = x1*cos(α) - y1*sin(α)

y2 = sin(α)*x1 + cos(α)*y1 + 0*z1


donc y2 = x1*sin(α) + y1*cos(α)

z2 = 0*x1 + 0*y1 + 1*z1


donc z2 = z1

Le cisaillement est une opération qui permet de déformer les objets. On


distingue trois types de cisaillement: Cx selon l’axe des X, Cy selon l’axe des
Y et Cz selon l’axe des Z. La matrice pour un cisaillement selon l’axe des X
est la suivante:

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 cisaillement:

x2 = 1*x1 +Cy*y1 + Cz*z1


donc x2 = x1 + y1*Cy + z1*Cz

y2 = 0*x1 + 1*y1 + 0*z1


donc y2 = y1
36 La programmation graphique 3D de WPF 4

z2 = 0*x1 + 0*y1 + 1*z1


donc z2 = z1

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

A2(0,2,0) A1(1,2,0) A2(5,2,0)

De la même façon, on aura la matrice pour un cisaillement selon l’axe des Y


Copyright 2011 Patrice REY

qui sera la suivante:


CHAPITRE 1 □ Les vecteurs et les matrices en 3D 37
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 cisaillement:

x2 = 1*x1 + 0*y1 + 0*z1


donc x2 = x1

y2 = Cx*x1 + 1*y1 + Cz*z1


donc y2 = x1*Cx + y1 + z1*Cz

z2 = 0*x1 + 0*y1 + 1*z1


donc z2 = z1

Et on aura la matrice pour un cisaillement selon l’axe des Z qui sera la suivante:

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 cisaillement:

x2 = 1*x1 + 0*y1 + 0*z1


donc x2 = x1

y2 = 0*x1 + 1*y1 + 0*z1


donc y2 = y1
38 La programmation graphique 3D de WPF 4

z2 = Cx*x1 + Cy*y1 + 1*z1


donc z2 = x1*Cx + y1*Cy +z1

2.2 - Définition des coordonnées homogènes

Les mathématiques des images par ordinateur sont intimement liées à la


multiplication matricielle. Comme nous venons de le voir dans le paragraphe
précédent, la translation d’un point ne correspond pas directement à une
multiplication matricielle, parce que la translation n’est pas une transformation
linéaire. Pour contourner cette difficulté, il est classique d’introduire ce que
l’on appelle les coordonnées homogènes.
Dans le système des coordonnées homogènes, on introduit une quatrième
coordonnée w, différente de zéro. Un point P(x,y,z) en coordonnées
euclidiennes s’écrira P(x/w,y/w,z/w,w) en coordonnées homogènes. Si w=1 par
exemple, alors les coordonnées du point P seront P(x,y,z,1). Ce changement
consistant à passer d’un triplet à un quadruplet de coordonnées s’appelle une
homogénéisation. Le cas où w est égale à zéro représente les points à l’infini.
De ce fait, les transformations 3D seront représentées par des matrices
de 4x4. Une matrice unique de 4x4 pourra désormais effectuer toutes les
transformations affines et non affines (translation, rotation, mise à l’échelle,
cisaillement). Il sera possible aussi de rassembler sous une seule matrice, une
composition de transformations simplement en effectuant une multiplication
des matrices.

2.3 - Transformations en coordonnées homogènes

Maintenant, un point P1(x1,y1,z1,1) donnera un point P2(x2,y2,z2,1) par une


matrice de transformation M de la façon suivante:
Copyright 2011 Patrice REY
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 39
Cette matrice de 4x4 permet de réaliser des transformations affines ou non
affines. Le calcul des coordonnées (x2,y2,z2) en fonction des coordonnées
(x1,y1,z1) se fera par le calcul matriciel de la façon suivante:

x2 = m11*x1 + m12*y1 + m13*z1


+ m14*1

y2 = m21*x1 + m22*y1 + m23*z1


+ m24*1

z2 = m31*x1 + m32*y1 + m33*z1


+ m34*1

1 = 0*x1 + 0*y1 + 0*z1 + 1*1


ce qui donne bien 1=1

Dans la matrice de transformation M, les composantes m11, m12, m13, m21,


m22, m23, m31, m32 et m33 seront utilisées pour effectuer des rotations, des
mises à l’échelle et des cisaillements, et les composantes m14, m24 et m34
seront utilisées pour la translation.
40 La programmation graphique 3D de WPF 4
composantes pour la rotation, la composantes pour la
mise à l’échelle et le cisaillement translation

La matrice de transformation concernant la translation donnera les coordonnées


(x2,y2,z2) suivantes en fonction du calcul matriciel: x2=x1+Tx, y2=y1+Ty et
z2=z1+Tz.

En 3D, une rotation fait tourner un ensemble de points ou d’objets d’un


angle α autour d’un axe de rotation. Contrairement à la rotation dans le plan,
l’axe autour duquel s’effectue la rotation 3D peut être une droite quelconque
dans l’espace. Il sera alors nécessaire de décomposer la rotation en trois
composantes: la rotation autour de l’axe des X, la rotation autour de l’axe
des Y et la rotation autour de l’axe des Z, ce qui donnera lieu à trois matrices
de rotation différentes. Comme dans le cas de la 2D, la rotation s’effectue
toujours par rapport à l’origine.
La matrice de rotation utilisée pour une rotation d’un angle α autour de l’axe
des X donnera x2=x1, y2=y1*cos(α)-z1*sin(α) et z2=y1*sin(α)+z1*cos(α).
Copyright 2011 Patrice REY
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 41
La matrice de rotation utilisée pour une rotation d’un angle α autour de l’axe
des Y donnera x2=x1*cos(α)+z1*sin(α), y2=y1 et z2=-x1*sin(α)+z1*cos(α).

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.

La mise à l’échelle modifie la taille d’un objet par rapport à l’origine en


l’agrandissant ou en le réduisant. La matrice de transformation pour une mise
à l’échelle donnera x2=x1*Sx, y2=y1*Sy et z2=z1*Sz.

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

Suivant l’axe des Y, la matrice de cisaillement donnera x2=x1,


y2=x1*Cx+y1+z1*Cz et z2=z1.

Suivant l’axe des Z, la matrice de cisaillement donnera x2=x1, y2=y1 et


z2=x1*Cx+y1*Cy+z1.

2.4 - La structure Point4D


Copyright 2011 Patrice REY

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:

La structure Matrix3D permet de représenter des transformations 3D affines


ou non affines. Les transformations non affines avec des valeurs non nulles
pour les composantes M14, M24 et M34, représentent des transformations en
projection perspective. Ces 16 composantes sont des propriétés publiques de
la structure.
La structure expose d’autres propriétés utiles pour la performance dans les
calculs sur les matrices qui sont:
• Determinant qui récupère le déterminant de cette structure Matrix3D.
• HasInverse qui obtient une valeur qui indique si la Matrix3D est réversible.
• Matrix3D.Identity qui est une propriété statique qui change une structure
Matrix3D en une matrice identité (matrice remplie de zéro sauf M11, M22,
M33 et M44 qui sont remplis de 1).
• IsAffine qui obtient une valeur qui indique si cette structure Matrix3D est
affine.
• IsIdentity qui détermine si cette structure Matrix3D est une matrice identité.
Jusqu’à présent, la notation des matrices de transformation que nous
avons utilisées, est celle qui est employée usuellement en algèbre linéaire.
Cependant WPF utilise des matrices transposées, ce qui explique la présence
des propriétés OffsetX, OffsetY et OffsetZ (qui correspondent à la translation)
Copyright 2011 Patrice REY

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

x2 = cos(-90)*5 + 0*0 + sin(-90)*0 + 0*1


=0
y2 = 0*5 + 1*0 + 0*0 + 0*1 = 0
z2 = -sin(-90)*5 + 0*0 + cos(-90)*0 + 0*1
=5

Dans la matrice de WPF, les lignes deviennent des colonnes. Le calcul matriciel
se fait donc de la façon suivante:

x2 = cos(-90)*5 + 0*0 + sin(-90)*0 + 0*1


=0
y2 = 0*5 + 1*0 + 0*0 + 0*1 = 0
z2 = -sin(-90)*5 + 0*0 + cos(-90)*0 + 0*1
=5

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.

composantes pour la rotation, la


mise à l’échelle et le cisaillement

composantes pour la translation

D’un point de vue du code, on écrira donc:

Point4D pt = new Point4D(5, 0, 0, 1);


double theta = -Math.PI / 2;
double sin = Math.Sin(theta);
double cos = Math.Cos(theta);
46 La programmation graphique 3D de WPF 4
Matrix3D matrice = new Matrix3D(
cos, 0, -sin, 0,
0, 1, 0, 0,
sin, 0, cos, 0,
0, 0, 0, 1);
Point4D pt_resultat = pt * matrice;

3.1 - Opérations sur la matrice 3D

L’UserControl OperationMatrix3D.xaml, dans le dossier chapitre01, visualise les


opérations effectuées sur des objets Matrix3D (figure 1-16).
Nous définissons une matrice mat1 de type Matrix3D qui représente une
translation de Tx=4 suivant l’axe des X, de Ty=2 suivant l’axe des Y et de
Tz=1 suivant l’axe des Z.

Matrix3D mat1 = new Matrix3D(


1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
4, 2, 1, 1);

La propriété HasInverse nous retourne un booléen pour signifier si la matrice


est inversible. Par définition, une matrice M est inversible si il existe une matrice
M-1 tel que M*M-1=Mid avec Mid qui est la matrice identité et M-1 qui est la
matrice inversée. Dans la littérature, une matrice non inversible est appelée
une matrice singulière, et une matrice inversible est appelée une matrice non
singulière ou régulière.
Si notre matrice mat1 est inversible alors on instancie une matrice mat2 en
lui affectant les valeurs de mat1. Puis on lui applique la méthode Invert() qui
calcule la matrice inverse.
Copyright 2011 Patrice REY

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

On instancie des points p1, p2 et p3 de type Point4D, en coordonnées


homogènes, en leur affectant les valeurs des coordonnées euclidiennes de
P1, P2 et P3. Pour obtenir les coordonnées de P1t, on instancie une nouvelle
structure p1t de type Point4D et on lui affecte le produit de p1 par la matrice
mat1 (opérateur * surchargé pour la structure Point4D).

Point4D p1 = new Point4D(3, 0, 1, 1);


x_infos.Text += «P1: = « + p1.ToString() + RC;
Point4D p1_t = new Point4D();
p1_t = p1 * mat1;
x_infos.Text += «P1t = P1 * mat1 « + p1_t.ToString() + RC;
Point4D p2 = new Point4D(3, 0, 5, 1);
x_infos.Text += «P2: = « + p2.ToString() + RC;
Point4D p2_t = new Point4D();
p2_t = p2 * mat1;
x_infos.Text += «P2t = P2 * mat1 « + p2_t.ToString() + RC;
Point4D p3 = new Point4D(3, 1, 5, 1);
50 La programmation graphique 3D de WPF 4
x_infos.Text += «P3: = « + p3.ToString() + RC;
Point4D p3_t = new Point4D();
p3_t = p3 * mat1;
x_infos.Text += «P3t = P3 * mat1 « + p3_t.ToString() + RC;

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

Copyright 2011 Patrice REY

calcul de w

Il est important d’effectuer le calcul à la main lors de l’apprentissage car cela


permet de s’entraîner au calcul matriciel, et dès fois, cela permet de corriger
des idées reçues.
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 51

3.2 - Transformations sur la matrice 3D

La structure Matrix3D possède des méthodes pour la rotation, la translation


et la mise à l’échelle des matrices. Voici les méthodes les plus fréquemment
utilisées pour les opérations sur les matrices 3D:
• Scale qui ajoute le Vector3D spécifié de mise à l’échelle à cette structure
Matrix3D.
• ScaleAt qui met à l’échelle cette structure Matrix3D avec le Vector3D spécifié
sur le Point3D spécifié.
• Translate qui ajoute une translation de l’offset spécifié à la structure Matrix3D
actuelle.
• Rotate qui ajoute une transformation de rotation au Matrix3D actuel.
• RotateAt qui fait pivoter la structure Matrix3D sur le Point3D spécifié.
• Invert qui inverse cette structure Matrix3D.
• Matrix3D.Multiply qui multiplie les matrices spécifiées (méthode statique).
• Transform qui transforme le Point3D spécifié par le Matrix3D et retourne
le résultat; qui transforme les objets Point3D spécifiés dans le tableau
par Matrix3D; qui transforme le Point4D spécifié par Matrix3D et retourne
le résultat; qui transforme les objets Point4D spécifiés dans le tableau par
Matrix3D et retourne le résultat; qui transforme le Vector3D spécifié par
Matrix3D; qui transforme les objets Vector3D spécifiés dans le tableau par
Matrix3D.
A noter qu’il n’y a pas de méthode pour la transformation d’inclinaison (ou
de cisaillement) pour la structure. Pour effectuer cette transformation, il faut
passer directement par les valeurs de la matrice Matrix3D.
Il y a aussi des méthodes suffixées par Prepend pour les transformations
de rotation, de mise à l’échelle et de translation. Par exemple, il y a pour
la translation les méthodes Translate et TranslatePrepend. La méthode Append
ajoute la matrice spécifiée à la matrice actuelle. C’est le mode par défaut. La
méthode Prepend ajoute initialement une matrice spécifiée à la matrice actuelle.
Les méthodes Append et Prepend définissent donc l’ordre d’application des
matrices. De ce fait, si la méthode Translate ajoute une translation de l’offset
spécifié à la structure Matrix3D actuelle, alors la méthode TranslatePrepend ajoute
initialement une translation de l’offset spécifié à la structure Matrix3D actuelle.
Append signifie que la nouvelle opération est appliquée après l’opération
précédente, et Prepend signifie que la nouvelle opération est appliquée avant
52 La programmation graphique 3D de WPF 4
l’opération précédente lors des associations de transformations.
L’UserControl TransformationMatrix3D.xaml, dans le dossier chapitre01, visualise
les transformations effectuées sur des objets Matrix3D (figure 1-18).
Figure 1-18

On instancie une matrice mat_orig de type Matrix3D avec ses 16 composantes.

//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»);

La méthode Scale permet d’ajouter le Vector3D spécifié de mise à l’échelle


(0.5 suivant X, 1.5 suivant Y et 2.5 suivant Z) à la structure mat_scale de type
Matrix3D (la matrice mat_scale est affectée par la structure mat_orig). Nous
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 53
obtenons comme résultat une matrice dont les composantes sont 0.5,3,7.5,4,
1,1.5,0,0,0,0,2.5,0,0.5,3,7.5,1.

//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))»);

Si nous effectuons le calcul à la main pour vérification, nous obtenons bien le


résultat trouvé.

La méthode ScalePrepend permet d’ajouter initialement le Vector3D de mise à


l’échelle spécifié (0.5 suivant X, 1.5 suivant Y et 2.5 suivant Z) à la structure
Matrix3D actuelle mat_scale_prepend (la matrice mat_scale_prepend est affectée
par la structure mat_orig). Nous obtenons comme résultat une matrice dont les
composantes sont 0.5,1,1.5,2,3,1.5,0,0,0,0,2.5,0,1,2,3,1.

Matrix3D mat_scale_prepend = mat_orig;


mat_scale_prepend.ScalePrepend(new Vector3D(0.5, 1.5, 2.5));
AfficherMatriceTexte(mat_scale_prepend, «mat_scale_prepend:
mat_scale_prepend.ScalePrepend(new Vector3D(0.5, 1.5, 2.5))»);

Si nous effectuons le calcul à la main pour vérification, nous obtenons bien le


résultat trouvé.
54 La programmation graphique 3D de WPF 4
La méthode Translate permet d’ajouter une translation (Vector3D de 100 suivant
X, 150 suivant Y et 200 suivant Z) de l’offset spécifié à la structure Matrix3D
actuelle mat_trans (la matrice mat_trans est affectée par la structure mat_orig).
Nous obtenons comme résultat une matrice dont les composantes sont 401,6
02,803,4,2,1,0,0,0,0,1,0,101,152,203,1.

//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))»);

Si nous effectuons le calcul à la main pour vérification, nous obtenons bien le


résultat trouvé.

La méthode TranslatePrepend permet d’ajouter initialement une translation


(Vector3D de 100 suivant X, 150 suivant Y et 200 suivant Z) de l’offset spécifié
à la structure Matrix3D actuelle mat_trans_prepend (la matrice mat_trans_prepend
est affectée par la structure mat_orig). Nous obtenons comme résultat une
matrice dont les composantes sont 1,2,3,4,2,1,0,0,0,0,1,0,401,352,503,401.

Matrix3D mat_trans_prepend = mat_orig;


mat_trans_prepend.TranslatePrepend(new Vector3D(100, 150, 200));
Copyright 2011 Patrice REY

AfficherMatriceTexte(mat_trans_prepend, «mat_trans_prepend:
mat_trans.TranslatePrepend(new Vector3D(100,150,200))»);

Si nous effectuons le calcul à la main pour vérification, nous obtenons bien le


résultat trouvé.
CHAPITRE 1 □ Les vecteurs et les matrices en 3D 55

La méthode Rotate ajoute une transformation de rotation à la structure


Matrix3D actuelle. Elle prend en paramètre un objet Quaternion. Un Quaternion
est une structure qui représente une rotation en 3D. Cette structure expose
principalement les propriétés suivantes:
• Axis qui obtient l’axe du quaternion.
• Angle qui obtient l’angle du quaternion, en degrés.
• IsIdentity qui obtient une valeur booléenne qui indique si le quaternion
spécifié est un quaternion Identity.
• Quaternion.Identity qui obtient le quaternion Identity (propriété statique).
• IsNormalized qui obtient une valeur booléenne qui indique si le quaternion
est normalisé ou pas.
Un des constructeurs avec paramètres permet d’instancier un Quaternion avec
un Vector3D (qui représente l’axe de rotation) et une valeur de type double qui
représente l’angle de rotation autour de l’axe spécifié (angle en degrés).
A noter que la coordonnée homogène w du quaternion est calculée
automatiquement.
Ici nous effectuons une rotation en instanciant une structure Quaternion.
Cette structure Quaternion est composé d’un axe de rotation spécifié par
un Vector3D(1,2,3) et un angle de rotation de 45 degrés autour de cette axe.
La matrice mat_rotate est affectée par la matrice mat_orig. Puis on effectue
la transformation de rotation. On obtient une matrice mat_rotate dont les
composantes (arrondies à 3 chiffres après la virgule) sont 1, 2, 3, 4, 0.931,
2.008, -0.316, 0, 0.441, -0.063, 0.895, 0, 1, 2, 3, 1.

//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)

Avec un angle de rotation de 45 degrés, les coordonnées homogènes du


quaternion sont donc de

Comme ce quaternion est normalisé, on peut construire la matrice de rotation


qui est

Copyright 2011 Patrice REY


CHAPITRE 1 □ Les vecteurs et les matrices en 3D 57
Maintenant si on applique la rotation à la matrice mat_orig, on obtient la mat_
rotate avec ses composantes. On retrouve bien les mêmes valeurs que par le
code procédural.

On effectue la même démarche avec la méthode RotatePrepend qui ajoute


initialement une rotation spécifiée par un quaternion à la structure Matrix3D
actuelle.

Matrix3D mat_rotate_prepend = mat_orig;


mat_rotate_prepend.RotatePrepend(
new Quaternion(new Vector3D(1, 2, 3), 45));
AfficherMatriceTexte(mat_rotate_prepend, 3, «mat_rotate_prepend:
mat_rotate_prepend.RotatePrepend(
new Quaternion(new Vector3D(1, 2, 3), 45))»);

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

En infographie, les projections transforment les points d’un système de


coordonnées de dimension 3 en des points d’un système de coordonnées de
dimension 2. Les objets 3D sont projetés dans un plan 2D, permettant de
visualiser à un moment donné le rendu d’une scène 3D.
Nous allons aborder au cours de ce chapitre les types de projection (perspective
et parallèle) en les mettant en œuvre pour se rendre compte de leurs différences.

1 - Les types de projection

Le rendu d’une scène 3D consiste à obtenir une visualisation réaliste de


la scène dans un plan de visualisation 2D. La projection d’un objet 3D est
définie par un ensemble de rayons de projection droits, appelés des projecteurs,
qui partent d’un centre de projection, passent par chaque point de l’objet, et
intersectent un plan de projection. Les intersections des rayons avec le plan
de projection forment la projection de l’objet. Le centre de projection se situe
généralement à une distance finie du plan de projection. Pour certains types
de projections, il est pourtant pratique de considérer un centre de projection
qui tend à être infiniment loin.
Les figures 2-1 et 2-2 visualisent deux projections différentes d’un segment de
droite. Comme la projection d’un segment de droite est elle-même un segment
de droite, il suffit de projeter les points extrêmes. La figure 2-1 visualise un
segment AB et sa projection perspective A’B’. La figure 2-2 visualise un segment
AB et sa projection parallèle A’B’ (les projecteurs AA’ et BB’ sont parallèles).
La classe de projections que nous étudions ici est connue sous le nom de
projections géométriques planes, puisque la projection se produit sur une
surface plane et non sur une surface courbée, et que les projecteurs sont des
droites et non des courbes. A contrario, les projections cartographiques ne
sont ni planes ni géométriques.
Les projections (terme usuellement employé pour les projections géométriques)
sont essentiellement de deux types: perspective et parallèle. Leur distinction
revient à la relation du centre de projection avec le plan de projection.
60 La programmation graphique 3D de WPF 4
Figure 2-1
A

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

1.1 - Les projections perspectives

Les projections perspectives de tout ensemble de lignes parallèles qui ne sont


pas parallèles au plan de projection convergent vers un point de fuite. Le
point de fuite peut être considéré comme la projection d’un point à l’infini. Il
existe donc un nombre infini de points de fuite dont chacun représente une
direction dans laquelle une droite peut être orientée. Si l’ensemble des lignes
est parallèle à l’un des axes principaux, le point de fuite est dit point de fuite
axial (ou principal). Il existe au plus trois points de fuite axiaux correspondant
au nombre d’axes que le plan de projection intersecte.
Par exemple, si le plan de projection n’intersecte que l’axe Z (et lui est donc
perpendiculaire), l’axe Z seulement possède un point de fuite axial car
les droites parallèles à l’axe X ou à l’axe Y sont aussi parallèles au plan de
projection et n’ont donc pas de point de fuite.
Les projections perspectives sont classées selon leur nombre de points de fuite
principaux, et donc selon le nombre d’axes qui traversent le plan de projection.
La figure 2-3 visualise une projection perspective d’un cube avec un point de
fuite. Dans cette projection, les lignes parallèles aux axes X et Y ne convergent
pas alors que celles parallèles à l’axe Z convergent.
Figure 2-3
point de fuite sur l’axe Z

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

La figure 2-5 visualise une perspective à trois points de fuite. Ce type


de perspective n’est pas très utilisé car il n’ajoute que peu de réalisme
supplémentaire.
Figure 2-5

Y
point de fuite
sur l’axe Z
point de fuite
sur l’axe X

Copyright 2011 Patrice REY

point de fuite sur l’axe Y


CHAPITRE 2 □ Les projections 63

1.2 - Les projections parallèles

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

Les projections orthonormales axonométriques utilisent des plans de projection


qui ne sont pas perpendiculaires à un axe principal. Elles représentent donc
plusieurs faces d’un même objet à la fois. Le parallélisme des droites est
préservé mais pas les angles.
La projection isométrique est une projection axonométrique couramment
utilisée. La normale du plan de projection, c’est-à-dire la direction de projection,
a des angles égaux avec chaque axe principal.
64 La programmation graphique 3D de WPF 4
Pour les projections obliques (second type de projection parallèle), la normale
de leur plan de projection est différente de leur direction de projection,
contrairement aux projections orthonormales. Elles combinent les propriétés
des projections orthonormales de façade, de plan de côté et celles de la
projection axonométrique.
La figure 2-7 représente les relations logiques entre les différents types de
projections. Le point commun de toutes les projections est qu’elles utilisent
un plan de projection et, soit un centre de projection pour les projections
perspectives, soit une direction de projection pour les projections parallèles.
Figure 2-7

projection géométrique plane

projection parallèle projection perspective

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

2 - La matrice de la projection perspective Copyright 2011 Patrice REY

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

En fixant un angle de vue, une distance du plan de projection (distance entre


le plan de projection et le centre de projection) et une distance du plan de loin
(distance entre le centre de projection et le plan de loin), les points en 3D dans
le volume d’observation peuvent être projetés dans le plan de projection.
Figure 2-9

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

L’UserControl ProjectionPerspective.xaml, dans le dossier chapitre02, permet


de mettre en œuvre l’utilisation d’une projection perspective en fixant les
différents paramètres de la caméra pour déterminer le rendu en 2D (figure
2-11).
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
Copyright 2011 Patrice REY

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.
CHAPITRE 2 □ Les projections 67
Figure 2-11
68 La programmation graphique 3D de WPF 4
Pour ce qui est de la réalisation de la géométrie du cube proprement dit et
de l’application des textures colorées, nous verrons cela plus en détail dans
un autre chapitre. Ce que nous allons surtout voir ici c’est l’incidence des
paramètres de la caméra sur le rendu de la scène.
L’espace 3D est visualisé par un objet Viewport3D (une sorte de Canvas pour un
rendu en 3D). On ajoute une caméra x_camera_matrice de type MatrixCamera.
Un objet MatrixCamera représente une caméra qui spécifie les transformations
de projection et d’affichage comme objets Matrix3D.

<Viewport3D ClipToBounds='True' Width='400' Height='400'>


<Viewport3D.Camera>
<MatrixCamera x:Name='x_camera_matrice' />
</Viewport3D.Camera>
...
</Viewport3D>

On définit la position de la caméra par m_pos_camera de type Point3D (la


caméra a une position donc des coordonnées 3D). La direction du regard de
la caméra est représentée par un vecteur m_look_direction de type Vector3D.
La direction du haut de la caméra est représentée par un vecteur m_up_
direction de type Vector3D. L’angle de vue est représenté par une valeur de type
double (exprimant une valeur en degré). Le plan de près m_plan_pres (plan de
projection) et le plan de loin m_plan_loin sont des valeurs de type double. Le
ratio m-aspect_ratio de type double représente le ratio concernant la largeur par
rapport à la hauteur du plan de projection.

private Point3D m_pos_camera = new Point3D(3, 4, 5);


private Vector3D m_look_direction = new Vector3D(-3, -4, -5);
private Vector3D m_up_direction = new Vector3D(0, 1, 0);
private double m_angle_vue = 60;
private double m_plan_pres = 1;
private double m_plan_loin = 100;
Copyright 2011 Patrice REY

private double m_aspect_ratio = 1;

La classe MatrixCamera expose principalement les propriétés suivantes:


• ViewMatrix qui définit une structure Matrix3D comme matrice de
transformation de l’affichage.
• ProjectionMatrix qui définit une structure Matrix3D comme matrice de
transformation de la projection.
CHAPITRE 2 □ Les projections 69
• Transform qui définit la transformation Transform3D appliquée à la caméra.
A chaque changement d’une position sur les glissières de type Slider, la
méthode FixerLaMatriceDeLaCamera() est appelée pour recalculer la matrice
Matrix3D de transformation de l’affichage (propriété ViewMatrix) et pour
recalculer la matrice Matrix3D de transformation de la projection (propriété
ProjectionMatrix).

private void FixerLaMatriceDeLaCamera() {


x_camera_matrice.ViewMatrix = Utils.FixerMatriceVue(
m_pos_camera, m_look_direction, m_up_direction);
x_camera_matrice.ProjectionMatrix =
Utils.FixerParametrePerspective(m_angle_vue, m_aspect_ratio,
m_plan_pres, m_plan_loin);
}

Pour calculer la matrice de transformation de l’affichage, on passe les


paramètres m_pos_camera, m_look_direction et m_up_direction à la méthode
FixerMatriceVue(..). Pour calculer la matrice de transformation de la projection,
on passe les paramètres m_angle_vue, m_aspect_ratio, m_plan_pres et m_plan_
loin à la méthode FixerParametrePerspective(..).
Avant de poursuivre, nous allons introduire les notions mathématiques de base
pour les projections géométriques planes. Nous avons le plan de projection de
la projection perspective qui est perpendiculaire à l’axe Z de la caméra en z=d.
Figure 2-12

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:

En multipliant chaque côté par d, on obtient:

La distance d est simplement un facteur d’échelle appliquée à xp et à yp. La


division par z rend la projection perspective des objets plus éloignés et plus
petites que celles des objets plus proches. Toute valeur de z est permise sauf
celle de z=0.
La transformation peut être représentée par la matrice

Multiplier le point P par la matrice donne un point général en coordonnées


homogènes (X,Y,Z,W): Copyright 2011 Patrice REY
CHAPITRE 2 □ Les projections 71
En divisant les composantes par W, c’est-à-dire z/d, et en éliminant la quatrième
coordonnée pour retourner en 3D, nous avons:

On obtient la coordonnée z transformée, c’est-à-dire d, qui est la position du


plan de projection dans la direction de l’axe Z.
A partir de là, on peut implémenter la méthode FixerMatriceVue(..) pour trouver
la matrice de transformation de l’affichage.

public static Matrix3D FixerMatriceVue(Point3D camera_position,


Vector3D look_direction, Vector3D up_direction) {
//normalisation des vecteurs
look_direction.Normalize();
up_direction.Normalize();
//definir les vecteurs XScale, YScale, et ZScale:
double denom = Math.Sqrt(1 -
Math.Pow(Vector3D.DotProduct(look_direction, up_direction), 2));
Vector3D XScale = Vector3D.CrossProduct(look_direction,
up_direction) / denom;
Vector3D YScale = (up_direction - (Vector3D.DotProduct(
up_direction, look_direction)) * look_direction) / denom;
Vector3D ZScale = look_direction;
//construction de la matrice M
Matrix3D M = new Matrix3D();
M.M11 = XScale.X;
M.M21 = XScale.Y;
M.M31 = XScale.Z;
M.M12 = YScale.X;
M.M22 = YScale.Y;
M.M32 = YScale.Z;
M.M13 = ZScale.X;
M.M23 = ZScale.Y;
M.M33 = ZScale.Z;
// Translater la camera de sa position a l’origine
Matrix3D translateMatrix = new Matrix3D();
translateMatrix.Translate(new Vector3D(-camera_position.X,
-camera_position.Y, -camera_position.Z));
//definir la reflexion sur l’axe z
Matrix3D reflectMatrix = new Matrix3D();
72 La programmation graphique 3D de WPF 4
reflectMatrix.M33 = -1;
//construction de la matrice d’affichage
Matrix3D viewMatrix = translateMatrix * M * reflectMatrix;
return viewMatrix;
}

Puis on peut implémenter la méthode FixerParametrePerspective(..) pour trouver


la matrice de transformation de projection.

public static Matrix3D FixerParametrePerspective(double angle_vue,


double aspect_ratio, double plan_pres, double plan_loin) {
Matrix3D matrice_perspective = new Matrix3D();
double grandeur_y = 1.0 / Math.Tan(angle_vue * Math.PI / 180 / 2);
double grandeur_x = grandeur_y / aspect_ratio;
matrice_perspective.M11 = grandeur_x;
matrice_perspective.M22 = grandeur_y;
matrice_perspective.M33 = plan_loin / (plan_pres - plan_loin);
matrice_perspective.M34 = -1.0;
matrice_perspective.OffsetZ =
plan_pres * plan_loin / (plan_pres - plan_loin);
matrice_perspective.M44 = 0;
return matrice_perspective;
}

Ainsi, en modifiant la position des Slider, on recalcule les matrices nécessaires


à l’affichage et à la projection. On obtient visuellement le rendu pour en
apprécier les incidences.

3 - La matrice de la projection parallèle

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

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.
Comme dans la projection parallèle, le centre de projection de la caméra se
trouve à l’infini, la droite qui sert à trouver le point projeté est parallèle à l’axe
Z de la caméra.
CHAPITRE 2 □ Les projections 73
Figure 2-13
plan de
projection
haut

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

Cette projection peut être représentée par la matrice:


Copyright 2011 Patrice REY
CHAPITRE 2 □ Les projections 75
Figure 2-15
76 La programmation graphique 3D de WPF 4
Le point projeté Pp a donc les coordonnées homogènes (x,y,d,1) par
transformation avec la matrice parallèle Mpar.

A chaque changement d’une position sur les glissières de type Slider, la


méthode FixerLaMatriceDeLaCamera() est appelée pour recalculer la matrice
Matrix3D de transformation de l’affichage (propriété ViewMatrix) et pour
recalculer la matrice Matrix3D de transformation de la projection (propriété
ProjectionMatrix).

private void FixerLaMatriceDeLaCamera() {


x_camera_matrice.ViewMatrix = Utils.FixerMatriceVue(
m_pos_camera, m_look_direction, m_up_direction);
x_camera_matrice.ProjectionMatrix = Utils.FixerParametreParallele(
m_largeur, m_largeur, m_plan_pres, m_plan_loin);
}

Pour calculer la matrice de transformation de l’affichage, on passe les


paramètres m_pos_camera, m_look_direction et m_up_direction à la méthode
FixerMatriceVue(..). Pour calculer la matrice de transformation de la projection,
on passe les paramètres m_largeur, m_plan_pres et m_plan_loin à la méthode
FixerParametreParallele(..).
Dans la projection parallèle, le plan de projection a des dimensions qui sont
fonction de la largeur et de la hauteur que l’on fixe. Ici on définit la largeur
Copyright 2011 Patrice REY

m_largeur identique à la hauteur.


On peut implémenter la méthode FixerParametreParallele (..) pour trouver la
matrice de transformation de projection.

public static Matrix3D FixerParametreParallele(double largeur,


double hauteur, double plan_pres, double plan_loin) {
Matrix3D matrice_parallele = new Matrix3D();
CHAPITRE 2 □ Les projections 77
matrice_parallele.M11 = 2 / largeur;
matrice_parallele.M22 = 2 / hauteur;
matrice_parallele.M33 = 1 / (plan_pres - plan_loin);
matrice_parallele.OffsetZ = plan_pres / (plan_pres - plan_loin);
return matrice_parallele;
}
CHAPITRE 3

Les transformations 3D

Le principe des transformations 3D est identique à celui des transformations


2D. Il consiste en l’application d’un système de coordonnées transformées
mappé sur le système de coordonnées initial en fonction d’une matrice. Il y
a des transformations de haut niveau et des transformations par application
directe d’une matrice.
Nous allons voir dans ce chapitre les classes de haut niveau que WPF fournit
pour une mise en œuvre plus simple.

1 - Les classes des 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

Copyright 2011 Patrice REY

2 - La transformation ScaleTransform3D

La classe ScaleTransform3D représente 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. Elle expose
CHAPITRE 3 □ Les transformations 3D 81
principalement les propriétés suivantes:
• CenterX qui définit la coordonnée x du point central de la transformation.
• CenterY qui définit la coordonnée y du point central de la transformation.
• CenterZ qui définit la coordonnée z du point central de la transformation.
• ScaleX qui définit le facteur d’échelle sur l’axe X.
• ScaleY qui définit le facteur d’échelle sur l’axe Y.
• ScaleZ qui définit le facteur d’échelle sur l’axe Z.
• Value qui obtient une représentation Matrix3D de cette transformation.
• IsAffine qui obtient une valeur qui indique si la transformation est affine
ou pas.
• Inverse qui obtient la transformation inverse de cet objet si elle existe.
Le constructeur par défaut de cette classe est ScaleTransform3D(). Plusieurs
constructeurs avec paramètres sont disponibles avec notamment:
• ScaleTransform3D(Vector3D) qui initialise une nouvelle instance de la classe
ScaleTransform3D à l’aide du Vector3D spécifié.
• ScaleTransform3D(Vector3D, Point3D) qui initialise une nouvelle instance de
la classe ScaleTransform3D avec les Vector3D et Point3D spécifiés.
• ScaleTransform3D(Double, Double, Double) qui initialise une nouvelle
instance de la classe ScaleTransform3D à l’aide des facteurs d’échelle
spécifiés.
• ScaleTransform3D(Double, Double, Double, Double, Double, Double) qui
initialise une nouvelle instance de la classe ScaleTransform3D à l’aide des
coordonnées de centre spécifiées et des facteurs d’échelle.
L’UserControl ExScaleTransform3d.xaml (figure 3-2), dans le dossier chapitre03,
permet de visualiser un cube coloré (avec six faces colorées différentes) pour
lequel on peut faire varier la mise à l’échelle suivant X, suivant Y et suivant
Z. Les deux modes de projection pour la visualisation du cube en 3D sont
accessibles par des RadioButton à cocher (projection perspective et projection
parallèle). Un conteneur de type Grid affiche les composantes de la matrice de
transformation ScaleTransform3D en cours.
On ajoute à la définition du cube coloré en XAML, un groupe, de type
Transform3DGroup, contenant les transformations 3D à effectuer. Une
transformation de mise à l’échelle x_cub_echelle de type ScaleTransform3D est
ajoutée, en lui fixant le centre de la transformation à la coordonnée CenterX=0,
CenterY=0 et CenterZ=0.
82 La programmation graphique 3D de WPF 4
Figure 3-2

Copyright 2011 Patrice REY


CHAPITRE 3 □ Les transformations 3D 83
<Transform3DGroup>
<TranslateTransform3D OffsetZ='1'></TranslateTransform3D>
<ScaleTransform3D
x:Name='x_cube_echelle'
CenterX='0' CenterY='0'
CenterZ='0'></ScaleTransform3D>
</Transform3DGroup>

Lors du déplacement d’une glissière de type Slider, on appelle la méthode


TransformerCube(). Elle permet de modifier les propriétés ScaleX, ScaleY et
ScaleZ de la transformation en fonction des valeurs relevées sur les glissières.

//appliquer une transformation de mise a l’echelle


private void TransformerCube() {
if (x_slider_scale_x != null && x_slider_scale_y != null
&& x_slider_scale_z!=null) {
x_cube_echelle.ScaleX = x_slider_scale_x.Value;
x_cube_echelle.ScaleY = x_slider_scale_y.Value;
x_cube_echelle.ScaleZ = x_slider_scale_z.Value;
AfficherMatriceEchelle();
}
}

La méthode AfficherMatriceEchelle() permet d’afficher le contenu des


composantes de la matrice de transformation de mise à l’échelle. On récupère
la matrice matrice_echelle de type Matrix3D par la propriété Value de la
transformation x_cube_echelle. Il suffit alors de lire les 16 composantes et de
les afficher au format texte.

private void AfficherMatriceEchelle() {


if (x_grid_matrice != null) {
Matrix3D matrice_echelle = x_cube_echelle.Value;
x_mat_m11.Text = Math.Round(matrice_echelle.M11, 2).ToString();
x_mat_m12.Text = Math.Round(matrice_echelle.M12, 2).ToString();
x_mat_m13.Text = Math.Round(matrice_echelle.M13, 2).ToString();
x_mat_m14.Text = Math.Round(matrice_echelle.M14, 2).ToString();
x_mat_m21.Text = Math.Round(matrice_echelle.M21, 2).ToString();
x_mat_m22.Text = Math.Round(matrice_echelle.M22, 2).ToString();
x_mat_m23.Text = Math.Round(matrice_echelle.M23, 2).ToString();
x_mat_m24.Text = Math.Round(matrice_echelle.M24, 2).ToString();
x_mat_m31.Text = Math.Round(matrice_echelle.M31, 2).ToString();
84 La programmation graphique 3D de WPF 4
x_mat_m32.Text = Math.Round(matrice_echelle.M32, 2).ToString();
x_mat_m33.Text = Math.Round(matrice_echelle.M33, 2).ToString();
x_mat_m34.Text = Math.Round(matrice_echelle.M34, 2).ToString();
x_mat_m41.Text =
Math.Round(matrice_echelle.OffsetX, 2).ToString();
x_mat_m42.Text =
Math.Round(matrice_echelle.OffsetY, 2).ToString();
x_mat_m43.Text =
Math.Round(matrice_echelle.OffsetZ, 2).ToString();
x_mat_m44.Text = Math.Round(matrice_echelle.M44, 2).ToString();
}
}

3 - La transformation TranslateTransform3D

La classe TranslateTransform3D représente un déplacement d’objet (une


translation) d’une certaine distance selon les axes X, Y et Z. Elle expose
principalement les propriétés suivantes:
• OffsetX qui définit le décalage selon l’axe X.
• OffsetY qui définit le décalage selon l’axe Y.
• OffsetZ qui définit le décalage selon l’axe Z.
• Value qui obtient une représentation Matrix3D de cette transformation.
• IsAffine qui obtient une valeur qui indique si la transformation est affine
ou pas.
• Inverse qui obtient la transformation inverse de cet objet si elle existe.
Le constructeur par défaut de cette classe est TranslateTransform3D(). Plusieurs
constructeurs avec paramètres sont disponibles avec notamment:
• TranslateTransform3D(Vector3D) qui initialise une nouvelle instance de la
classe TranslateTransform3D, à l’aide de l’offset Vector3D spécifié.
• TranslateTransform3D(Double, Double, Double) qui initialise une nouvelle
instance de la classe TranslateTransform3D à l’aide de l’offset spécifié.
L’UserControl ExTranslateTransform3d.xaml (figure 3-3), dans le dossier chapitre03,
Copyright 2011 Patrice REY

permet de visualiser un cube pour lequel on peut faire varier le déplacement


suivant X, suivant Y et suivant Z. Les deux modes de projection pour la
visualisation du cube en 3D sont accessibles par des RadioButton à cocher
(projection perspective et projection parallèle). Un conteneur de type Grid
affiche les composantes de la matrice de transformation TranslateTransform3D
en cours.
CHAPITRE 3 □ Les transformations 3D 85
Figure 3-3
86 La programmation graphique 3D de WPF 4
On ajoute à la définition du cube coloré en XAML, un groupe, de type
Transform3DGroup, contenant les transformations 3D à effectuer. Une
transformation de translation x_cube_trans de type TranslateTransform3D est
ajoutée, en lui fixant les propriétés à OffsetX=0, OffsetY=0 et OffsetZ=0.

<Transform3DGroup>
<TranslateTransform3D
x:Name='x_cube_trans'
OffsetX='0' OffsetY='0'
OffsetZ='0'>
</TranslateTransform3D>
</Transform3DGroup>

Lors du déplacement d’une glissière de type Slider, on appelle la méthode


TransformerCube(). Elle permet de modifier les propriétés OffsetX, OffsetY et
OffsetZ de la transformation en fonction des valeurs relevées sur les glissières.

//appliquer une transformation de translation


private void TransformerCube() {
if (x_slider_trans_x != null && x_slider_trans_y != null
&& x_slider_trans_z != null) {
x_cube_trans.OffsetX = x_slider_trans_x.Value;
x_cube_trans.OffsetY = x_slider_trans_y.Value;
x_cube_trans.OffsetZ = x_slider_trans_z.Value;
AfficherMatriceEchelle();
}
}

4 - La transformation RotateTransform3D

La classe RotateTransform3D représente une transformation affine qui spécifie


une transformation de rotation dans l’espace 3D. Elle expose principalement
les propriétés suivantes:
Copyright 2011 Patrice REY

• CenterX qui définit la coordonnée x du Point3D autour duquel pivoter.


• CenterY qui définit la coordonnée y du Point3D autour duquel pivoter.
• CenterZ qui définit la coordonnée z du Point3D autour duquel pivoter.
• Rotation qui définit un objet Rotation3D qui spécifie la rotation.
• Value qui récupère un objet Matrix3D qui représente la rotation.
• IsAffine qui obtient une valeur qui indique si la transformation est affine
ou pas.
CHAPITRE 3 □ Les transformations 3D 87
• Inverse qui obtient la transformation inverse de cet objet si elle existe.
Le constructeur par défaut de cette classe est RotateTransform3D(). Plusieurs
constructeurs avec paramètres sont disponibles avec notamment:
• RotateTransform3D(Rotation3D) qui initialise une nouvelle instance de la
classe RotateTransform3D avec la rotation spécifiée.
• RotateTransform3D(Rotation3D, Point3D) qui initialise une nouvelle instance
de la classe RotateTransform3D avec la rotation et le centre spécifiés.
• RotateTransform3D(Rotation3D, Double, Double, Double) qui initialise une
nouvelle instance de la classe RotateTransform3D avec la rotation et les
coordonnées du centre spécifiées.
L’UserControl ExRotateTransform3d.xaml (figure 3-4), dans le dossier chapitre03,
permet de visualiser un cube pour lequel on peut le faire pivoter d’un angle
autour des axes X, Y et Z. Les deux modes de projection pour la visualisation
du cube en 3D sont accessibles par des RadioButton à cocher (projection
perspective et projection parallèle). Un conteneur de type Grid affiche les
composantes de la matrice de transformation RotateTransform3D en cours.
On ajoute à la définition du cube coloré en XAML, un groupe, de type
Transform3DGroup, contenant les transformations 3D à effectuer. Une
transformation de rotation x_cube_rotate de type RotateTransform3D est ajoutée,
en lui fixant les propriétés à CenterX=0, CenterY=0 et CenterZ=0.

<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

Copyright 2011 Patrice REY

//valeur qui change sur un slider


private void x_slider_ValueChanged(object sender, RoutedPropertyChang
edEventArgs<double> e) {
Slider recup = (Slider)sender;
switch (recup.Name) {
case «x_slider_x»:
x_text_slider_x.Text = «angle X = « +
Math.Round(x_slider_x.Value, 2).ToString();
CHAPITRE 3 □ Les transformations 3D 89
FaireTournerCube(new Vector3D(1, 0, 0), x_slider_x.Value);
break;
case «x_slider_y»:
x_text_slider_y.Text = «angle Y = « +
Math.Round(x_slider_y.Value, 2).ToString();
FaireTournerCube(new Vector3D(0, 1, 0), x_slider_y.Value);
break;
case «x_slider_z»:
x_text_slider_z.Text = «angle Z = « +
Math.Round(x_slider_z.Value, 2).ToString();
FaireTournerCube(new Vector3D(0, 0, 1), x_slider_z.Value);
break;
}
AfficherMatriceEchelle();
}

Pour effectuer la rotation autour d’un axe, on instancie un objet


AxisAngleRotation3D en lui passant un vecteur représentant l’axe et une valeur
représentant l’angle. Puis on affecte cet objet à la propriété Rotation de la
transformation RotateTransform3D.

//faire tourner d’un angle


private void FaireTournerCube(Vector3D axe, double angle) {
x_cube_rotate.Rotation = new AxisAngleRotation3D(axe, angle);
}

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>

On peut aussi exprimer en XAML la rotation par l’intermédiaire d’un objet


QuaternionRotation3D. Pour cela on écrira:

<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

La classe MatrixTransform3D 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. Elle expose principalement les propriétés:
• Matrix qui définit un Matrix3D qui spécifie une transformation 3D.
• Value qui obtient une représentation de la matrice de la transformation
3D.
• Inverse qui obtient la transformation inverse de cet objet, si elle existe.
• IsAffine qui obtient une valeur qui indique si la transformation est affine
ou pas.
Le constructeur par défaut est MatrixTransform3D(). Le constructeur avec
paramètres est MatrixTransform3D(Matrix3D) qui initialise une nouvelle
instance de la classe MatrixTransform3D à l’aide du Matrix3D spécifié.
L’UserControl ExMatrixTransform3d.xaml (figure 3-5), dans le dossier chapitre03,
permet de visualiser un cube pour lequel on peut modifier les valeurs de la
matrice de transformation par l’intermédiaire des glissières. Il y a 16 glissières
qui correspondent aux 16 composantes de la matrice de transformation. Trois
boutons supplémentaires sont ajoutés avec un réglage prédéfini concernant
les inclinaisons. Les deux modes de projection pour la visualisation du cube
en 3D sont accessibles par des RadioButton à cocher (projection perspective et
projection parallèle). Un conteneur de type Grid affiche les composantes de
la matrice de transformation MatrixTransform3D en cours. Il est ainsi possible
d’effectuer sur le cube toutes les transformations que l’on désire seulement en
agissant sur les glissières.
On ajoute à la définition du cube coloré, en XAML, un groupe, de type
Copyright 2011 Patrice REY

Transform3DGroup, contenant les transformations 3D à effectuer. Une


transformation par matrice x_cube_matrice de type MatrixTransform3D est
ajoutée. Pour initialiser la propriété Matrix de la matrice de transformation, on
ajoute un nœud <MatrixTransform3D.Matrix> et on définit les 16 composantes
de l’objet Matrix3D (propriétés M11, M12, M13, M14, M21, M22, M23, M24,
M31, M32, M33, M34, OffsetX, OffsetY, OffsetZ et M44).
CHAPITRE 3 □ Les transformations 3D 91
Figure 3-5
92 La programmation graphique 3D de WPF 4
<Transform3DGroup>
<MatrixTransform3D
x:Name='x_cube_matrice'>
<MatrixTransform3D.Matrix>
<Matrix3D M11='1' M12='0'
M13='0' M14='0' M21='0'
M22='1' M23='0' M24='0'
M31='0' M32='0' M33='1'
M34='0' OffsetX='0'
OffsetY='0' OffsetZ='0'
M44='1'></Matrix3D>
</MatrixTransform3D.Matrix>
</MatrixTransform3D>
</Transform3DGroup>

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().

//changement de valeur des slider


private void x_slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
Slider recup = (Slider)sender;
switch (recup.Name) {
case «x_slider_m11»:
x_text_m11.Text = «M11 = « +
Math.Round(x_slider_m11.Value, 2).ToString();
break;
case «x_slider_m12»:
x_text_m12.Text = «M12 = « +
Math.Round(x_slider_m12.Value, 2).ToString();
break;
...
}
FixerMatriceTransformation();
Copyright 2011 Patrice REY

La méthode FixerMatriceTransformation() permet de modifier la matrice


de la transformation x_cube_matrice. Pour cela, on instancie une nouvelle
matrice matrice de type Matrix3D. On affecte à ses 16 composantes la valeur
correspondante en fonction de la propriété Value des glissières. Puis on affecte
cette matrice à la propriété Matrix de la matrice de transformation x_cube_
CHAPITRE 3 □ Les transformations 3D 93
matrice.

//definir la matrice de la transformation


private void FixerMatriceTransformation() {
...
Matrix3D matrice = new Matrix3D();
matrice.M11 = x_slider_m11.Value;
matrice.M12 = x_slider_m12.Value;
matrice.M13 = x_slider_m13.Value;
matrice.M14 = x_slider_m14.Value;
matrice.M21 = x_slider_m21.Value;
matrice.M22 = x_slider_m22.Value;
matrice.M23 = x_slider_m23.Value;
matrice.M24 = x_slider_m24.Value;
matrice.M31 = x_slider_m31.Value;
matrice.M32 = x_slider_m32.Value;
matrice.M33 = x_slider_m33.Value;
matrice.M34 = x_slider_m34.Value;
matrice.OffsetX = x_slider_m41.Value;
matrice.OffsetY = x_slider_m42.Value;
matrice.OffsetZ = x_slider_m43.Value;
matrice.M44 = x_slider_m44.Value;
x_cube_matrice.Matrix = matrice;
AfficherMatriceTransform();
...
}

Nous avons vu que WPF ne fournissait pas de transformation de haut niveau


pour exécuter une inclinaison en 3D. Pour cela, il faut agir directement sur la
matrice. Il y a trois boutons x_btn_incliner_x, x_btn_incliner_y et x_btn_incliner_z
qui possèdent un réglage prédéfini pour visualiser une inclinaison.
Par exemple, le bouton x_btn_incliner_x visualise une inclinaison du cube
suivant l’axe X (avec une valeur d’inclinaison égale à 2). Le bouton x_btn_
incliner_y visualise une inclinaison du cube suivant l’axe Y (avec une valeur
d’inclinaison égale à -1). Le bouton x_btn_incliner_z visualise une inclinaison
du cube suivant l’axe Y (avec une valeur d’inclinaison égale à 2).

//reglage predefini : inclinaison suivant x


private void x_btn_incliner_x_Click(object sender,
RoutedEventArgs e) {
RemiseAZero();
94 La programmation graphique 3D de WPF 4
x_slider_m21.Value = 2;
FixerMatriceTransformation();
}
//reglage predefini : inclinaison suivant y
private void x_btn_incliner_y_Click(object sender,
RoutedEventArgs e) {
RemiseAZero();
x_slider_m32.Value = -1;
FixerMatriceTransformation();
}
//reglage predefini : inclinaison suivant z
private void x_btn_incliner_z_Click(object sender,
RoutedEventArgs e) {
RemiseAZero();
x_slider_m23.Value = 2;
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

Il est possible d’effectuer une combinaison de transformations de haut niveau


simultanément. Pour cela, il suffit d’ajouter dans un objet TransformGroup3D
les transformations à effectuer et avec l’ordre que l’on souhaite. Pour une
combinaison d’une mise à l’échelle x_echelle et d’une rotation x_rotate, on aura:

<Transform3DGroup>
<ScaleTransform3D x:Name=»x_echelle»>
</ ScaleTransform3D>
<RotateTransform3D
x:Name=»x_rotate»
CenterX=»0» CenterY=»0» CenterZ=»0»>
Copyright 2011 Patrice REY

</RotateTransform3D>
</Transform3DGroup>

Ensuite, par code procédural, on peut accéder à ces transformations puis


modifier leurs paramètres en fonction du contexte donné.
CHAPITRE 4

La scène 3D

La scène 3D est un objet complexe. C’est l’endroit où l’on positionne des


éléments en 3D dans le but d’effectuer un rendu en 2D. Ce sont ces éléments
qui permettent de donner un réalisme à la scène 3D.
Dans ce chapitre nous allons voir comment s’organise une scène 3D au travers
des éléments nécessaires à sa composition avec notamment le Viewport3D,
les maillages et la géométrie 3D de base, les conteneurs qui reçoivent les
objets 3D, les différentes sources de lumière, les différentes caméras et leurs
paramètres.

1 - Visualisation de la scène

La création d’une scène 3D avec l’objet le plus simple qu’est le triangle, ne


peut se faire qu’au travers d’un ensemble d’étapes qui sont:
• l’ajout d’un contrôle Viewport3D qui restitue le contenu 3D dans les limites
de disposition 2D de l’élément Viewport3D.
• l’ajout des objets 3D qui dérivent généralement des objets ModelVisual3D
ou ModelUIElement3D.
• l’ajout d’une source de lumière qui illumine la scène.
• l’application de textures à l’objet 3D qui déterminent l’apparence de sa
surface.
• le positionnement de caméra, qui projette l’objet 3D dans une représentation
2D, et à partir de laquelle on peut visualiser la scène.
Cette liste d’étapes est juste une liste basique mais incontournable pour
réaliser une scène 3D.
Une scène 3D peut être très complexe en pratique, en contenant de nombreux
objets 3D, plusieurs sources d’illumination, de nombreuses caméras pour
avoir différents points de visualisation, ainsi que différents matériaux pour la
composition des objets. Tout cet ensemble concoure au réalisme d’une scène
3D mais oblige le programmeur à effectuer la même démarche quel que soit
l’objet 3D à visualiser.
96 La programmation graphique 3D de WPF 4

2 - Le contrôle Viewport3D

Le contenu graphique 3D dans WPF est encapsulé dans un élément Viewport3D


qui peut participer à la structure du document à deux dimensions. Le système
graphique traite Viewport3D comme un élément visuel à deux dimensions
semblable à bien d’autres éléments dans WPF. Le Viewport3D fonctionne comme
une fenêtre d’affichage dans une scène tridimensionnelle. Plus précisément,
c’est une surface sur laquelle une scène 3D est projetée. La classe Viewport3D
expose les propriétés suivantes:
• Camera qui définit un objet caméra qui projette le contenu en 3D de la
fenêtre Viewport3D sur la surface 2D de Viewport3D.
• Children qui est une collection des enfants Visual3D de Viewport3D.
Un Viewport3D est un contrôle (il hérite de FrameworkElement). Il contient donc
(figure 4-1) une collection de Visual3D dans sa propriété Children (propriété
implicite en XAML) c’est-à-dire tous les objets 3D qui composent la scène.
Mais un Viewport3D n’affiche rien sans caméra ni éclairage. Une caméra doit
être définie dans la propriété Camera.
Figure 4-1

Copyright 2011 Patrice REY


CHAPITRE 4 □ La scène 3D 97
En pratique, un Viewport3D sera ajouté dans une fenêtre de type Window ou
dans un contrôle de type UserControl. Généralement on l’encadre dans un
contrôle Border de façon à identifier la zone 3D. Ce Viewport3D doit avoir ses
propriétés Width et Height explicitement définies. De plus, sa propriété héritée
ClipToBounds sera fixée à true (la propriété UIElement.ClipToBounds définit une
valeur qui indique s’il faut découper le contenu de cet élément, ou le contenu
qui provient d’éléments enfants de cet élément, pour l’ajuster aux dimensions
de l’élément contenant; il s’agit d’une propriété de dépendance). La structure
XAML sera principalement de la forme suivante:

<Border BorderBrush='Black' BorderThickness='1' Width='400'


Height='400' Canvas.Left='12' Canvas.Top='68'
Background='Black'>
<!-- le controle viewport3d -->
<Viewport3D ClipToBounds='True' Width='400' Height='400'>
<!-- les ressources du viewport3d -->
<Viewport3D.Resources>
...
</Viewport3D.Resources>
<!-- les caméras du viewport3d -->
<Viewport3D.Camera>
...
</Viewport3D.Camera>
<!-- le conteneur des objets 3D du viewport3d -->
<ContainerUIElement3D>
...
</ContainerUIElement3D>
</Viewport3D>
</Border>

3 - La création de modèles 3D

La création d’objets 3D peut se faire de deux façons différentes: soit en utilisant


la classe ModelVisual3D, soit en utilisant la classe UIElement3D.

3.1 - La classe ModelVisual3D

La classe ModelVisual3D, issue du framework .NET 3.0, est une classe


bivalente (figure 4-2). Elle permet d’afficher un modèle défini par un Model3D
(GeometryModel3D ou Model3DGroup) spécifié dans la propriété Content. Elle
98 La programmation graphique 3D de WPF 4
peut contenir d’autres objets Visual3D spécifiés dans la propriété Children.
Figure 4-2

Les modèles composites peuvent être créés au moyen de la propriété Children,


mais également en définissant un contenu de type Model3DGroup dans la
propriété Content qui permet de regrouper plusieurs Model3D.
L’interactivité sur ModelVisual3D peut être obtenue au moyen du test d’atteinte
(hit testing). Le test d’atteinte est une technique de bas niveau que nous ne
détaillerons pas car elle est avantageusement remplacée par les fonctionnalités
de UIElement3D.
Voici un exemple de code représentant un modèle ModelVisual3D composite
regroupant plusieurs GeometryModel3D: Copyright 2011 Patrice REY

<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<GeometryModel3D ...></GeometryModel3D>
<GeometryModel3D ...></GeometryModel3D>
<GeometryModel3D ...></GeometryModel3D>
...
</Model3DGroup>
CHAPITRE 4 □ La scène 3D 99
</ModelVisual3D.Content>
</ModelVisual3D>

Voici un exemple de code représentant un modèle composite regroupant


plusieurs ModelVisual3D dans la propriété Children d’un ModelVisual3D:

<ModelVisual3D>
<ModelVisual3D>
...
</ModelVisual3D>
<ModelVisual3D>
...
</ModelVisual3D>
...
</ModelVisual3D>

3.2 - La classe UIElement3D

La classe abstraite UIElement3D est apparue dans le framework .NET 3.5,


accompagnée de deux classe héritées figure 4-3): ModelUIElement3D et
ContainerUIElement3D. Ces classes offrent une alternative intéressante à
ModelVisual3D car elles sont plus évoluées sur le plan fonctionnel et aussi car
chacune est spécialisée:
• UIElement3D hérite de Visual3D et apporte une gestion de haut niveau de
l’interactivité (gestion du focus, support du clavier, du stylet, du toucher et
de la souris); son interface est proche de celle de UIElement.
• ModelUIElement3D permet d’afficher un modèle défini par un Model3D
(GeometryModel3D ou Model3DGroup) spécifié dans la propriété Model.
• ContainerUIElement3D peut contenir d’autres objets Visual3D spécifiés dans
la propriété Children.
Il existe toujours deux techniques pour créer des modèles composites, mais ici
en exploitant deux classes différentes:
• assembler des Visual3D au sein d’un ContainerUIElement3D.
• assembler des Model3D au sein d’un Model3DGroup assigné à la propriété
Model d’un ModelUIElement3D.
Voici un exemple de modèle ModelUIElement3D composite regroupant plusieurs
GeometryModel3D:
100 La programmation graphique 3D de WPF 4
Figure 4-3

<ModelUIElement3D>
<ModelUIElement3D.Model>
<Model3DGroup>
<GeometryModel3D></GeometryModel3D>
<GeometryModel3D></GeometryModel3D>
<GeometryModel3D></GeometryModel3D>
...
</Model3DGroup>
</ModelUIElement3D.Model>
</ModelUIElement3D>

Voici un exemple de modèle composite regroupant plusieurs


Copyright 2011 Patrice REY

ContainerUIElement3Ddans la propriété Children d’un ContainerUIElement3D:

<ContainerUIElement3D>
<ContainerUIElement3D>
...
</ContainerUIElement3D>
<ContainerUIElement3D>
CHAPITRE 4 □ La scène 3D 101
...
</ContainerUIElement3D>
...
</ContainerUIElement3D>

3.3 - La classe GeometryModel3D

La classe abstraite Model3D représente une description d’objet 3D. Un objet


Model3D peut être utilisé pour définir le visuel d’un objet Visual3D. Comme le
montre l’arbre d’héritage de la figure 4-4, les classes concrètes qui dérivent de
Model3D sont:
• GeometryModel3D qui définit un modèle basé sur une géométrie 3D.
• Model3DGroup qui permet l’utilisation de plusieurs modèles Model3D en
tant qu’unité.

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

Copyright 2011 Patrice REY


CHAPITRE 4 □ La scène 3D 103
La classe MeshGeometry3D définit le maillage d’un modèle 3D au moyen des
propriétés suivantes:
• Positions qui représente une collection des points formant les sommets
(vertex) du maillage (du type Point3D).
• TriangleIndices qui représente une collection des indices des positions
formant les triangles des faces du maillage (du type Int32).
• Normals qui représente une collection des vecteurs normaux qui sont
utilisés pour le calcul de l’angle de réflexion des rayons lumineux sur la
surface.
• TextureCoordinates qui représente une collection des points 2D de la texture
(le contenu dans le pinceau) correspondant aux positions des sommets du
maillage.
Pour que la production d’un contenu soit visible, la géométrie doit au moins
définir les propriétés Positions et TriangleIndices. Une valeur par défaut est
calculée automatiquement pour la propriété Normals. L’initialisation de
TextureCoordinates n’est pas nécessaire tant qu’une matière sans texture est
utilisée.
La base des maillages est le triangle. Un modèle 3D est un assemblage de
facettes triangulaires. Le triangle est la surface la plus simple qui puisse exister.
En effet, trois points suffisent pour définir un triangle.
Les triangles du modèle sont définis au moyen des points formant les sommets
du maillage. La propriété TriangleIndices contient des séries de trois valeurs
correspondant aux indices des sommets (qui sont indiqués dans la propriété
Positions) formant chacune un triangle. L’ordre dans lequel les indices d’un
triangle sont spécifiés détermine le côté, avant ou arrière, sur lequel la face
est représentée par rapport à la position du point de vue courant. Pour être
face à l’observateur, les indices doivent être spécifiés dans l’ordre inverse des
aiguilles d’une montre.
La figure 4-6 visualise la réalisation d’un modèle MeshGeometry3D constitué
d’un simple triangle. Le triangle est composé des trois points A(0,3,0), B(4,0,0)
et C(0,0,3). On affecte à chaque point un numéro d’indice, avec par exemple
0 pour A, 1 pour B et 2 pour C. Quand on observe le triangle, on veut que la
face de devant soit celle qui est vue (face qui comporterait une couleur ou une
texture par exemple). La démarche consiste à exprimer les points dans l’ordre
des indices en écrivant:
Positions = «0 3 0, 4 0 0, 0 0 3»
(la notation suivante est acceptée aussi: Positions = «0,3,0 4,0,0 0,0,3»)
104 La programmation graphique 3D de WPF 4
Puis il faut exprimer l’ordre des indices par:
TriangleIndices = «0 2 1»
(la notation suivante est acceptée aussi: TriangleIndices = «0,2,1»)
L’ordre «0 2 1» est dans le sens antihoraire, donc la face du triangle que nous
voyons, sera la face qui comportera la couleur ou la texture. La face arrière de
ce triangle comportera une couleur ou une texture différente.
Figure 4-6 Y

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. Dans la propriété Geometry de GeometryModel3D, on affecte


le MeshGeometry3D (avec les propriétés Positions et TriangleIndices). Dans la
propriété Material de GeometryModel3D, on ajoute un objet DiffuseMaterial
dont sa propriété Brush est fixée à Red (la face avant du triangle 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
CHAPITRE 4 □ La scène 3D 105
triangle sera colorée en jaune).

<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>

La figure 4-7 visualise la réalisation d’un modèle MeshGeometry3D qui


représente un rectangle et qui est constitué par deux simples triangles. Le
premier triangle est composé des trois points A(4,2,0), B(4,0,0) et C(0,0,3). Le
deuxième triangle est composé des trois points C(0,0,3), D(0,2,3) et A(4,2,0).
On affecte à chaque point un numéro d’indice, avec par exemple 0 pour A, 1
pour B, 2 pour C et 3 pour D. Quand on observe le rectangle, on veut que la
face de devant soit celle qui est vue (face qui comporterait une couleur ou une
texture par exemple). La démarche consiste à exprimer les points dans l’ordre
des indices en écrivant:
Positions = «4 2 0, 4 0 0, 0 0 3, 0 2 3»
Puis il faut exprimer l’ordre des indices par:
TriangleIndices = «2 1 0, 0 3 2»
L’ordre «2 1 0» est dans le sens antihoraire, donc la face du triangle que nous
voyons, sera la face qui comportera la couleur ou la texture. La face arrière
de ce triangle comportera une couleur ou une texture différente. Il en est de
même pour l’ordre «0 3 2».
Pour afficher ce rectangle, 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
ModelVisual3D. Dans la propriété Geometry de GeometryModel3D, on affecte
le MeshGeometry3D (avec les propriétés Positions et TriangleIndices). Dans la
propriété Material de GeometryModel3D, on ajoute un objet DiffuseMaterial dont
106 La programmation graphique 3D de WPF 4
Figure 4-7
Y

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.

5.1 - La surface mate

Une surface mate est définit par un objet DiffuseMaterial. La caractéristique


d’une surface mate est de diffuser la lumière qu’elle reçoit et dont le rendu est
fonction de l’éclairage. Les parties à l’ombre sont noires.
La classe DiffuseMaterial 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 avec un
éclairage normal.
• AmbientColor qui définit la couleur réfléchie pour la texture de la matière
avec un éclairage de type AmbientLight.
Par exemple, la face d’un rectangle peinte en rouge 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'
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

5.2 - La surface lumineuse

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>

5.3 - Combinaison de matières

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>

5.4 - La réflexion spéculaire

Un objet SpecularMaterial permet l’application d’un pinceau 2D, tel qu’un


SolidColorBrush ou un TileBrush, à un modèle 3D éclairé de manière spéculaire.
La classe SpecularMaterial gère la réflexion spéculaire en ajoutant de la
brillance à une surface définie avec d’autres matières. Elle s’utilise toujours
en association avec d’autres objets Material au sein d’un MaterialGroup. La
brillance est constituée de reflets en fonction de l’éclairage et de la position du
point de vue.
La classe SpecularMaterial expose principalement les propriétés suivantes:
Copyright 2011 Patrice REY

• SpecularPower qui indique l’intensité des reflets.


• Color qui indique la couleur des reflets.
Cela s’exprimera par exemple en XAML par:

<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

La représentation d’une scène 3D dépend de la position du point de vue, et


de la direction du regard. Ces paramètres sont gérés par un objet Camera. La
projection 2D d’une scène 3D est définit par la caméra qui spécifie quelle
partie de la scène 3D devant être restituée par l’élément Viewport3DVisual ou
Viewport3D.
La classe de base abstraite Camera gère les différents types de caméra. La figure
4-10 visualise son arbre d’héritage. Ses classes dérivées sont:
• ProjectionCamera qui est une classe de base abstraite pour les caméras à
projection perspective et à projection orthographique (ou parallèle).
• MatrixCamera qui définit une caméra qui spécifie les transformations de
projection et d’affichage comme des objets de type Matrix3D.
La classe abstraite ProjectionCamera possède deux classes dérivées qui sont:
• PerspectiveCamera qui représente une caméra avec une projection de type
perspective.
• OrthographicCamera qui représente une caméra avec une projection de type
orthographique (ou parallèle).
Un objet PerspectiveCamera définit une projection avec de la perspective c’est-
à-dire contenant un point vers lequel convergent toutes les lignes de fuite.
114 La programmation graphique 3D de WPF 4

Figure 4-10

Un objet OrthographicCamera définit une projection orthographique sans


perspective. Les dimensions de la représentation d’un élément sont fixes et
indépendantes de sa position.
Les classes PerspectiveCamera et OrthographicCamera exposent principalement
les propriétés suivantes:
Copyright 2011 Patrice REY

• Position qui définit la position de la caméra en coordonnées universelles.


• LookDirection qui définit un vecteur Vector3D qui représente la direction de
visée de la caméra en coordonnées universelles.
• UpDirection qui définit un vecteur Vector3D qui indique l’inclinaison de la
caméra; par défaut, sa valeur est (0,1,0) ce qui signifie que la verticale de
l’image correspond à l’axe Y, valeurs positives vers le haut.
CHAPITRE 4 □ La scène 3D 115
• NearPlaneDistance et FarPlaneDistance qui indiquent les limites de la zone
de rendu des éléments visuels respectivement à l’avant-plan et à l’arrière-
plan par rapport à la caméra; les parties des éléments au-delà des limites
sont invisibles; les valeurs par défaut sont respectivement 0.125 et l’infini.
De plus, l’objet PerspectiveCamera expose la propriété FielOfView qui détermine
le champ de vision horizontal de la caméra. Une valeur élevée correspond
à une vue de type grand-angle, et une valeur faible correspond à une vue
de type téléobjectif. La figure 4-11 visualise les principales propriétés de la
caméra perspective.

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

Quant à l’objet OrthographicCamera, il expose la propriété Width qui représente


la largeur de la zone d’affichage. La figure 4-12 visualise les principales
propriétés de la caméra orthographique.
116 La programmation graphique 3D de WPF 4
Figure 4-12

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

Copyright 2011 Patrice REY


CHAPITRE 5

Les modèles 3D basiques

Les modèles 3D sont composés par des assemblages dont l’assemblage de


base est le triangle. Deux triangles qui sont joints par leur hypoténuse forment
une facette rectangulaire.
Nous allons voir dans ce chapitre comment réaliser des assemblages basiques
et comment les visualiser. A partir de ces assemblages, nous verrons la
réalisation des principaux modèles 3D basiques que sont le cube, la sphère, le
cylindre, le cône et le tore.

1 - La modélisation basique

La base des maillages est le triangle. Un modèle 3D est un assemblage de


facettes triangulaires. Le triangle est la surface la plus simple qui puisse exister.
En effet, trois points suffisent pour définir un triangle.

1.1 - La facette triangulaire

L’UserControl ExFacetteTriangle.xaml, dans le dossier chapitre05, visualise un


triangle rectangle dont la face avant est colorée en bleu et la face arrière est
colorée en jaune (figure 5-1). Une caméra est positionnée dans l’espace 3D, elle
vise l’origine du repère 3D. Deux glissières, une verticale et une horizontale,
permettent de faire tourner le triangle autour de l’axe des Y et autour de l’axe
des X. Deux RadioButton permettent de sélectionner une surface pour la face
avant du triangle. Deux autres RadioButton permettent de sélectionner un
éclairage particulier.
Nous allons employer la démarche qui a été vue au chapitre 4 pour la
visualisation. On commence par ajouter un contrôle de rendu 3D de type
Viewport3D avec ses propriétés With et Height fixées explicitement (400 pixels
dans notre exemple). La propriété ClipToBounds est fixée à true pour éviter
tous les débordements de la zone de rendu. La couleur du fond de la zone de
rendu est celle du contrôle Border (propriété Background fixée à Black).
118 La programmation graphique 3D de WPF 4
Figure 5-1

Copyright 2011 Patrice REY


CHAPITRE 5 □ Les modèles 3D basiques 119
<!-- zone de rendu 3d ***************************** -->
<Border BorderBrush='Black' BorderThickness='1' Width='400'
Height='400'
Canvas.Left='12' Canvas.Top='68' Background='Black'>
<Viewport3D ClipToBounds='True' Width='400' Height='400'
Name='x_viewport'>
...
</Viewport3D>
</Border>

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).

<Viewport3D ClipToBounds='True' Width='400' Height='400'


Name='x_viewport'>
<!-- cameras ****************************** -->
<Viewport3D.Camera>
<PerspectiveCamera x:Name='x_camera' Position='0,0,5'
LookDirection='0,0,-5' FieldOfView='60'
UpDirection='0 1 0'></PerspectiveCamera>
</Viewport3D.Camera>
...
</Viewport3D>

Les objets 3D sont positionnés dans un objet ContainerUIElement3D qui est


affecté à la propriété Children de Viewport3D (propriété implicite en XAML
donc elle n’a pas besoin d’être mentionnée avec <Viewport3D.Children>). On
choisit un objet ModelUIElement3D pour restituer un modèle 3D qui prend en
charge l’entrée, le focus et les événements.
120 La programmation graphique 3D de WPF 4
<Viewport3D ClipToBounds='True' Width='400' Height='400'
Name='x_viewport'>
...
<!-- conteneur des modeles **************** -->
<ContainerUIElement3D>
<ModelUIElement3D>
...
</ModelUIElement3D>
</ContainerUIElement3D>
...
</Viewport3D>

La propriété implicite Model de ModelUIElement3D (<ModelUIElement3D.


Model>) est constituée d’un objet x_objets_3d, de type Model3DGroup, qui est
composé des différents maillages qui sont apportés à la scène 3D.

<Viewport3D ClipToBounds='True' Width='400' Height='400'


Name='x_viewport'>
...
<!-- conteneur des modeles **************** -->
<ContainerUIElement3D>
<ModelUIElement3D>
<Model3DGroup x:Name='x_objets_3d'>
...
</Model3DGroup>
</ModelUIElement3D>
</ContainerUIElement3D>
...
</Viewport3D>

Un éclairage x_lumiere, de type AmbientLight, permet d’éclairer la scène. On


fixe la propriété Color de AmbientLight à White pour un éclairage blanc.

<!-- conteneur des modeles **************** -->


Copyright 2011 Patrice REY

<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>

On positionne un groupe x_triangle, de type Model3DGroup, qui va contenir


la définition du triangle. La figure 5-2 visualise la réalisation du triangle avec
3 points et 3 indices.
Figure 5-2
Y

(0,2,0) indice 2

face arrière jaune


face avant bleue
(2,0,0) indice 1
(0,0,0) indice 0 X

angle 30°

(0,0,5) caméra
Z

Un objet GeometryModel3D, intitulé x_triangle_geomodel3d, constitue


le modèle 3D du triangle. La propriété Geometry de GeometryModel3D
(<GeometryModel3D.Geometry>) contient la définition du maillage du triangle.
Ce maillage est un MeshGeometry3D avec sa propriété Positions fixée à «0 0
0, 2 0 0, 0 2 0», et sa propriété TriangleIndices fixée à «0 1 2». La propriété
Material de GeometryModel3D (<GeometryModel3D.Material>) contient la
définition de la surface avant du triangle. On l’initialise avec une surface mate
x_surface_1 peinte en bleu (Brush = «DodgerBlue»). La propriété BackMaterial de
122 La programmation graphique 3D de WPF 4
GeometryModel3D (<GeometryModel3D. BackMaterial>) contient la définition
de la surface arrière du triangle. On l’initialise avec une surface mate peinte
en jaune (Brush = «Yellow»).

<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>

Pour permettre de faire tourner le triangle autour des axes X et Y, on lui


ajoute un groupe de transformations, de type Transform3DGroup, à sa
propriété Transform (<Model3DGroup.Transform>). Dans ce groupe, pour faire
tourner autour de l’axe Y, on ajoute un objet RotateTransform3D intitulé x_
triangle_rotation_xz, et pour faire tourner autour de l’axe X, on ajoute un objet
Copyright 2011 Patrice REY

RotateTransform3D intitulé x_triangle_rotation_yz.

<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>

La glissière x_slider_hori, de type Slider, va permettre de faire tourner le triangle


autour de l’axe Y. Il faut définir la propriété Rotation de la transformation x_
triangle_rotation_xz, en lui affectant une rotation autour d’un axe. Pour cela, on
instancie un objet AxisAngleRotation3D, qui permet une rotation dans l’espace
3D autour d’une droite, en lui affectant un vecteur représentant la droite (ici
un Vector3D(0,1,0) qui représente l’axe Y), et une valeur d’angle (angle défini
par la valeur de la glissière x_slider_hori.Value). On fait de même pour l’autre
glissière en affectant à x_triangle_rotation_yz un objet AxisAngleRotation3D, dont
le vecteur représentant l’axe X est un Vector3D(1,0,0) et dont l’angle est défini
par la valeur x_slider_verti.Value.
Figure 5-3
124 La programmation graphique 3D de WPF 4
La figure 5-3 montre l’effet obtenu par la modification des valeurs sur les
glissières.

//valeur changé sur slider horizontal


private void x_slider_hori_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
if (this.IsLoaded == true) {
Slider recup_slider = (Slider)sender;
switch (recup_slider.Name) {
case «x_slider_hori»:
x_triangle_rotation_xz.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 1, 0), x_slider_hori.Value);
break;
case «x_slider_verti»:
x_triangle_rotation_yz.Rotation = new AxisAngleRotation3D(
new Vector3D(1, 0, 0), x_slider_verti.Value);
break;
}
AfficherInfosDansGrille();
}
}

La modification des surfaces du triangle en cliquant sur les RadioButton x_


rbtn_surface_1 et x_rbtn_surface_2, se fait par code procédural. Une surface
mate diff1, de type DiffuseMaterial, est instanciée avec une couleur Brushes.
DodgerBlue. Puis elle est affectée à la propriété Material du modèle 3D x_
triangle_geomodel3d. Une surface brillante emi1, de type EmissiveMaterial, est
instanciée avec une couleur Brushes.DodgerBlue. Puis elle est affectée à la
propriété Material du modèle 3D x_triangle_geomodel3d.

//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

DiffuseMaterial diff1 = new DiffuseMaterial(Brushes.DodgerBlue);


x_triangle_geomodel3d.Material = diff1;
}
if (rbtn.Name == «x_rbtn_surface_2») {
EmissiveMaterial emi1 = new EmissiveMaterial(Brushes.DodgerBlue);
x_triangle_geomodel3d.Material = emi1;
}
}
CHAPITRE 5 □ Les modèles 3D basiques 125
La modification de l’éclairage de la scène en cliquant sur les RadioButton x_rbtn_
eclairage_1 et x_rbtn_eclairage_2, se fait par code procédural. Le RadioButton
x_rbtn_eclairage_1 permet d’éclairer la scène avec une lumière blanche de type
AmbientLight. Le RadioButton x_rbtn_eclairage_2 permet d’éclairer la scène avec
une lumière blanche de type SpotLight.
Quand le RadioButton x_rbtn_eclairage_1 est coché, il faut vider la liste des
enfants du Model3DGroup x_lumiere. On instancie un éclairage de type
AmbienLight dont la propriété Color est affectée par une couleur blanche (Colors.
White). Puis on l’ajoute à la liste des enfants de x_lumiere par la méthode Add(..)
appliquée à la propriété Children de Model3DGroup.
Quand le RadioButton x_rbtn_eclairage_2 est coché, il faut vider la liste des
enfants du Model3DGroup x_lumiere. On instancie un objet SpotLight. On lui fixe
sa propriété Color à Colors.White. On fixe sa propriété Position par un Point3D
de coordonnées (0,0,5). On règle la direction de l’axe du cône de lumière en le
faisant pointer sur l’origine du repère. Il faut donc affecter un vecteur Vector3D
dont l’orientation va de la position du spot au repère d’origine (propriété
Direction affectée par un Vector3D(0,0,-5) représentant cette direction). On règle
les angles du cône qui éclaire la scène en fixant les propriétés InnerConeAngle
et OuterConeAngle. Puis on ajoute l’objet SpotLight à la liste des enfants de
x_lumiere. La figure 5-4 visualise les propriétés du cône de lumière de type
SpotLight et l’effet obtenu dans l’exemple.

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);
}
}

1.2 - La facette rectangulaire

L’UserControl ExFacetteRectangle.xaml, dans le dossier chapitre05, visualise


un rectangle dont les faces, avant et arrière, sont remplies par des textures
issues de deux images différentes (figure 5-5). Une caméra est positionnée
dans l’espace 3D, elle vise l’origine du repère 3D. Une glissière horizontale
permet de faire tourner le rectangle autour de l’axe Z. Une deuxième glissière
permet de faire tourner la caméra autour de l’axe Y pour avoir un point de
vue différent.
La caméra, de type PerspectiveCamera, a une position de (0,10,10) et vise
l’origine du repère. On lui affecte une transformation x_cam_rotate de type
RotateTransform3D.
Copyright 2011 Patrice REY

<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>

La rotation de la caméra autour de l’axe Y se fait par une rotation de type


AxisAngleRotation dont l’axe est représenté par le vecteur Vector3D(0,1,0) et
dont l’angle est représenté par la valeur de la glissière x_slider_cam.

//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();
}
}

Comme le montre la figure 5-6, un rectangle est réalisé à partir de deux


triangles. Le premier triangle est composé des points (0,0,0), (0,0,4) et (6,0,4)
avec les indices respectifs 0, 1 et 2. Le deuxième triangle est composé des
points (6,0,4), (6,0,0) et (0,0,0) avec les indices respectifs 2, 3 et 0. Le maillage,
de type MeshGeometry3D, a la propriété Positions = «0 0 0, 0 0 4, 6 0 4, 6 0 0» et
la propriété TriangleIndices = «0 0, 0 1, 1 1, 1 0».

<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>

Pour appliquer un placage de texture sur la face avant, il faut définir la


propriété Material de GeometryModel3D en lui affectant un objet DiffuseMaterial
CHAPITRE 5 □ Les modèles 3D basiques 129
Figure 5-6

texture face avant


gle 30°
(0,0) (6,0,0) indice 3
(0,0,0) indice 0 X
rotation rectangle (1,0)

(0,0,4) indice 1 (6,0,4) indice 2


(0,1) (1,1)

Z texture face arrière

par exemple. Puis il faut appliquer à la propriété Brush de DiffuseMaterial, un


objet ImageBrush qui correspond à la texture à appliquer. Cet ImageBrush a
sa propriété ImageSource qui référence une image (ici elle référence l’image
texture-avant.png qui se trouve dans le sous-dossier image du dossier contenu).
Le procédé est le même pour appliquer un placage de texture sur la face arrière
en définissant la propriété BackMaterial de GeometryModel3D.
Ce placage de texture ne peut être réalisé que si la propriété TextureCoordinates
est explicitement exprimée. La propriété TextureCoordinates de la géométrie
MeshGeometry3D permet d’établir la correspondance entre les sommets du
maillage et les points 2D du contenu d’un pinceau évolué (tel que VisualBrush
ou ImageBrush). Pour exécuter ce placage, il faut tenir compte du fait que
l’axe Y en 2D est inversé par rapport à l’axe Y en 3D. Les coordonnées 2D
sont exprimées en relatif par une valeur entre 0 et 1. La figure 5-6 visualise
130 La programmation graphique 3D de WPF 4
les coordonnées de la texture à appliquer, ce qui donne une propriété
TextureCoordinates = «0 0, 0 1, 1 1, 1 0».

<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

qui tourne autour de l’axe Z, avec la visibilité de la texture de la face avant et


celle de la face arrière.

2 - Le cube

La modélisation d’un cube peut se faire de différentes façons. Nous allons


aborder la modélisation d’un cube comme un objet unique avec une couleur
CHAPITRE 5 □ Les modèles 3D basiques 131
uniforme (ou une texture uniforme) appliquée à tout le cube. Puis nous
verrons comment modéliser un cube, face par face, de façon à colorer les faces
différemment. Enfin nous verrons, par extension de la modélisation du cube,
comment créer des axes de coordonnées.

2.1 - Un cube avec une couleur uniforme

L’UserControl ExCubeUniforme.xaml, dans le dossier chapitre05, visualise un


cube dont les faces sont remplies par une texture issue d’une image (figure
5-7). Une caméra est positionnée dans l’espace 3D, elle vise l’origine du repère
3D. Trois glissières horizontales permettent de faire tourner le cube autour
des axes X, Y et Z.
Nous allons modéliser le cube comme étant une entité unique. Cela veut dire
que l’application d’une couleur ou d’une texture sera la même sur toutes les
faces du cube (propriété Material et BackMaterial). La figure 5-8 permet de
visualiser la fabrication du cube.
On instancie un GeometryModel3D intitulé x_cube. Dans sa propriété Geometry,
on affecte une géométrie MeshGeometry3D qui représente la géométrie du cube.
La démarche consiste en premier lieu à identifier tous les sommets du cube par
des indices et à noter les coordonnées de tous ces points (par exemple, l’indice
0 avec les coordonnées (0,0,1), l’indice 1 avec les coordonnées (1,0,1) et ainsi
de suite). Nous avons 8 points dont les indices vont de 0 à 7. On exprime donc
la propriété Positions de MeshGeometry3D par le relevé des positions de ces 8
points.

<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

Copyright 2011 Patrice REY


CHAPITRE 5 □ Les modèles 3D basiques 133
Figure 5-8

4 (0,1,0) 5 (1,1,0)

4 (0,1,0) 3 (0,1,1) Y 3 (0,1,1) 2 (1,1,1)

7 (0,0,0) 0 (0,0,1) 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

0 (0,0,1) 1 (1,0,1) 2 (1,1,1) 5 (1,1,0)

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)

0 (0,0,1) 1 (1,0,1) 7 (0,0,0) 6(1,0,0)

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)

ordre du premier triangle: 1 2 3


ordre du deuxième triangle: 3 0 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>

Pour pouvoir faire tourner le cube suivant X, Y et Z, on affecte à la propriété


Transform de GeometryModel3D, un groupe de transformations Transform3DGroup
dans lequel on ajoute trois objets RotateTransform3D, intitulés x_cube_rotate_x,
x_cube_rotate_y et x_cube_rotate_z.

<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>

Le gestionnaire x_slider_ValueChanged permet de gérer la rotation du cube


selon les axes en instanciant des objets AxisAngleRotation3D avec les paramètres
demandés.
CHAPITRE 5 □ Les modèles 3D basiques 135
//valeur changé sur slider horizontal
private void x_slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
if (this.IsLoaded == true) {
Slider recup_slider = (Slider)sender;
switch (recup_slider.Name) {
case «x_slider_axe_y»:
x_cube_rotate_y.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 1, 0), x_slider_axe_y.Value);
break;
case «x_slider_axe_x»:
x_cube_rotate_x.Rotation = new AxisAngleRotation3D(
new Vector3D(1, 0, 0), x_slider_axe_x.Value);
break;
case «x_slider_axe_z»:
x_cube_rotate_z.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 0, 1), x_slider_axe_z.Value);
break;
}
AfficherInfosDansGrille();
}

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.

<!-- groupe cube -->


<GeometryModel3D x:Name='x_cube'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/image/texture-2.png'
Stretch='Fill'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
...
<GeometryModel3D.Geometry>
136 La programmation graphique 3D de WPF 4
<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'
TextureCoordinates='0 0, 1 0, 1 1, 0 1,
0 0, 1 0, 1 1, 0 1,
0 0, 1 0, 1 1, 0 1,
0 0, 1 0, 1 1, 0 1,
0 0, 1 0, 1 1, 0 1,
0 0, 1 0, 1 1, 0 1'></MeshGeometry3D>
</GeometryModel3D.Geometry>
...
</GeometryModel3D>

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.

2.2 - Un cube avec des faces colorées

L’UserControl ExCubeFacette.xaml, dans le dossier chapitre05, visualise un cube


dont les faces sont remplies par une texture issue d’une image (figure 5-10).
La texture est différente pour chaque face de façon à personnaliser le cube.
Une caméra est positionnée dans l’espace 3D, elle vise l’origine du repère 3D.
Trois glissières horizontales permettent de faire tourner le cube autour des
axes X, Y et Z.
La définition de la géométrie du cube et l’application des textures sur les faces
vont être réalisées par code procédural. Dans la partie XAML, on se contente
de positionner un Model3DGroup x_scene_3d dans un ModelUIElement3D, à
Copyright 2011 Patrice REY

l’intérieur d’un ContainerUIElement3D, pour recevoir les objets 3D. On ajoute


un éclairage de type AmbientLight.

<!-- conteneur des modeles **************** -->


<Viewport3D.Children>
<ContainerUIElement3D>
<ModelUIElement3D>
CHAPITRE 5 □ Les modèles 3D basiques 137
Figure 5-10
138 La programmation graphique 3D de WPF 4
<Model3DGroup x:Name='x_scene_3d'>
<!-- groupe: lumiere -->
<Model3DGroup x:Name='x_lumiere'>
<AmbientLight></AmbientLight>
</Model3DGroup>
</Model3DGroup>
</ModelUIElement3D>
</ContainerUIElement3D>
</Viewport3D.Children>

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.

private void AjouterCube() {


//creer cube de largeur 1.5 et face texturees
List<string> liste_chemin_texture = new List<string>();
liste_chemin_texture.Add(«contenu/image/face-cube-avant.png»);
liste_chemin_texture.Add(«contenu/image/face-cube-droite.png»);
liste_chemin_texture.Add(«contenu/image/face-cube-arriere.png»);
liste_chemin_texture.Add(«contenu/image/face-cube-gauche.png»);
Copyright 2011 Patrice REY

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

0 (a,0,a) 4(a,0,0) 7 (0,0,0) 3 (0,0,a) 1 (a,a,a) 3 (0,0,a)


1 (a,a,a) 5 (a,a,0) 6 (0,a,0) 2 (0,a,a) 5 (a,a,0) 7 (0,0,0)
2 (0,a,a) 1 (a,a,a) 5 (a,a,0) 6 (0,a,0) 6 (0,a,0) 4(a,0,0)
3 (0,0,a) 0 (a,0,a) 4(a,0,0) 7 (0,0,0) 2 (0,a,a) 0 (a,0,a)

La méthode CreerCubeAvecLargeurCote(..) commence par générer une collection


de points collect_pts, composée de Point3D correspondant aux positions des
sommets en fonction de la longueur cote d’un côté.

private Model3DGroup CreerCubeAvecLargeurCote(double cote,


List<string> liste_chemin_texture) {
140 La programmation graphique 3D de WPF 4
Model3DGroup modele_3d_gp = new Model3DGroup();
Point3DCollection collect_pts = new Point3DCollection();
collect_pts.Add(new Point3D(cote, 0, cote));
collect_pts.Add(new Point3D(cote, cote, cote));
collect_pts.Add(new Point3D(0, cote, cote));
collect_pts.Add(new Point3D(0, 0, cote));
collect_pts.Add(new Point3D(cote, 0, 0));
collect_pts.Add(new Point3D(cote, cote, 0));
collect_pts.Add(new Point3D(0, cote, 0));
collect_pts.Add(new Point3D(0, 0, 0));
...
return modele_3d_gp;
}

Puis, au Model3DGroup modele_3d_gp, on ajoute les 6 faces du cube, de type


GeometryModel3D, générées par la méthode CreerFaceCube(..) pour laquelle on
lui passe en paramètre quatre points formant les sommets d’un carré et un
chemin correspondant à une texture à appliquer.

private Model3DGroup CreerCubeAvecLargeurCote(double cote,


List<string> liste_chemin_texture) {
Model3DGroup modele_3d_gp = new Model3DGroup();
...
GeometryModel3D face_avant = CreerFaceCube(collect_pts[0],
collect_pts[1], collect_pts[2], collect_pts[3],
liste_chemin_texture[0]);
modele_3d_gp.Children.Add(face_avant);
GeometryModel3D face_droite = CreerFaceCube(collect_pts[4],
collect_pts[5], collect_pts[1], collect_pts[0],
liste_chemin_texture[1]);
modele_3d_gp.Children.Add(face_droite);
GeometryModel3D face_arriere = CreerFaceCube(collect_pts[7],
collect_pts[6], collect_pts[5], collect_pts[4],
liste_chemin_texture[2]);
modele_3d_gp.Children.Add(face_arriere);
Copyright 2011 Patrice REY

GeometryModel3D face_gauche = CreerFaceCube(collect_pts[3],


collect_pts[2], collect_pts[6], collect_pts[7],
liste_chemin_texture[3]);
modele_3d_gp.Children.Add(face_gauche);
GeometryModel3D face_dessus = CreerFaceCube(collect_pts[1],
collect_pts[5], collect_pts[6], collect_pts[2],
liste_chemin_texture[4]);
modele_3d_gp.Children.Add(face_dessus);
CHAPITRE 5 □ Les modèles 3D basiques 141
GeometryModel3D face_dessous = CreerFaceCube(collect_pts[3],
collect_pts[7], collect_pts[4], collect_pts[0],
liste_chemin_texture[5]);
modele_3d_gp.Children.Add(face_dessous);
return modele_3d_gp;
}

Par la méthode CreerFaceCube(..), on génère un maillage GeometryModel3D


qui représente un carré (ou un rectangle) à l’aide des Point3D pt0, pt1, pt2
et pt3. La géométrie de ce GeometryModel3D est obtenue par la méthode
CreerGeometrieFace(..) en prenant en paramètre une collection contenant les 4
sommets. Cette géométrie est affectée à la propriété Geometry de modele_3d.
Pour appliquer la texture, on instancie un objet DiffuseMaterial. On affecte à sa
propriété Brush, un objet ImageBrush dont sa propriété ImageSource pointe sur
le chemin de la ressource de la texture. Cet objet intitulé diffuse est affecté à
la propriété Material de modele_3d. On affecte à la propriété BackMaterial un
pinceau de couleur jaune pour matérialiser l’arrière des faces.

private GeometryModel3D CreerFaceCube(Point3D pt0, Point3D pt1,


Point3D pt2, Point3D pt3, string chemin_texture) {
GeometryModel3D modele_3d = new GeometryModel3D();
Point3DCollection collect_sommet = new Point3DCollection();
collect_sommet.Add(pt0);
collect_sommet.Add(pt1);
collect_sommet.Add(pt2);
collect_sommet.Add(pt3);
modele_3d.Geometry = CreerGeometrieFace(collect_sommet);
DiffuseMaterial diffuse = new DiffuseMaterial();
ImageBrush img_brush = new ImageBrush();
img_brush.ImageSource = new BitmapImage(new Uri(
«pack://application:,,/» + chemin_texture, UriKind.Absolute));
diffuse.Brush = img_brush;
modele_3d.Material = diffuse;
modele_3d.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
return modele_3d;
}

Le maillage d’une face est réalisé par la méthode CreerGeometrieFace(..). Il


consiste à ajouter les positions des sommets à la propriété Positions de maillage
de type MeshGeometry3D. Puis l’ordre des indices est ajouté à la propriété
TriangleIndices. La propriété TextureCoordinates, quant à elle, reçoit les positions
142 La programmation graphique 3D de WPF 4
du placage d’une image sur la face. La figure 5-12 visualise cette étape de
maillage.
Figure 5-12
pt1 texture
pt2
(0,0) (1,0)
une face avec les
sommets pt0, pt1,
pt2 et pt3

pt3 pt0

(0,1) (1,1)

maillage.Positions.Add(pt0) maillage.TriangleIndices.Add(1) maillage.TextureCoordinates.Add(


maillage.Positions.Add(pt1) maillage.TriangleIndices.Add(2) new Point(1, 1))
maillage.Positions.Add(pt2) maillage.TriangleIndices.Add(3) maillage.TextureCoordinates.Add(
maillage.Positions.Add(pt3) maillage.TriangleIndices.Add(3) new Point(1, 0))
maillage.TriangleIndices.Add(0) maillage.TextureCoordinates.Add(
maillage.TriangleIndices.Add(1) new Point(0, 0))
maillage.TextureCoordinates.Add(
new Point(0, 1))

private MeshGeometry3D CreerGeometrieFace(Point3DCollection


collect_pts) {
MeshGeometry3D maillage = new MeshGeometry3D();
foreach (Point3D pt in collect_pts) {
maillage.Positions.Add(pt);
}
maillage.TriangleIndices.Add(1);
maillage.TriangleIndices.Add(2);
maillage.TriangleIndices.Add(3);
maillage.TriangleIndices.Add(3);
maillage.TriangleIndices.Add(0);
maillage.TriangleIndices.Add(1);
maillage.TextureCoordinates.Add(new Point(1, 1));
maillage.TextureCoordinates.Add(new Point(1, 0));
maillage.TextureCoordinates.Add(new Point(0, 0));
Copyright 2011 Patrice REY

maillage.TextureCoordinates.Add(new Point(0, 1));


return maillage;
}

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.

private RotateTransform3D m_cube_rotate_y = null;


private RotateTransform3D m_cube_rotate_z = null;
private RotateTransform3D m_cube_rotate_x = null;
...
private void AjouterCube() {
...
Transform3DGroup gp_transform = new Transform3DGroup();
m_cube_rotate_y = new RotateTransform3D();
gp_transform.Children.Add(m_cube_rotate_y);
m_cube_rotate_z = new RotateTransform3D();
gp_transform.Children.Add(m_cube_rotate_z);
m_cube_rotate_x = new RotateTransform3D();
gp_transform.Children.Add(m_cube_rotate_x);
groupe_cube.Transform = gp_transform;
x_scene_3d.Children.Add(groupe_cube);
}

De façon à faire tourner le cube en fonction des modifications des trois


glissières, il suffit d’affecter un objet AxisAngleRotation3D à la propriété Rotation
des objets RotateTransform3D. L’axe de rotation est passé en paramètre par le
vecteur correspondant. La valeur de l’angle de rotation est passée en paramètre
par la valeur de la glissière modifiée.

//valeur changée sur slider


private void x_slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
if (this.IsLoaded == true) {
Slider recup_slider = (Slider)sender;
switch (recup_slider.Name) {
case «x_slider_axe_y»:
m_cube_rotate_y.Rotation= new AxisAngleRotation3D(
new Vector3D(0, 1, 0), x_slider_axe_y.Value);
break;
case «x_slider_axe_x»:
m_cube_rotate_x.Rotation = new AxisAngleRotation3D(
new Vector3D(1, 0, 0), x_slider_axe_x.Value);
break;
case «x_slider_axe_z»:
144 La programmation graphique 3D de WPF 4
m_cube_rotate_z.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 0, 1), x_slider_axe_z.Value);
break;
}
AfficherInfosDansGrille();
}
}

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.3 - Les axes de coordonnées X, Y et Z

L’UserControl ExAxes.xaml, dans le dossier chapitre05, visualise un cube centré


sur les axes de coordonnées X, Y et Z (figure 5-13). Le cube possède des faces
texturées. Une caméra est positionnée dans l’espace 3D, elle vise l’origine du
repère 3D. Trois glissières horizontales permettent de faire tourner la scène
3D dans son intégralité autour des axes X, Y et Z.
La réalisation des axes des coordonnées peut se voir comme une extension de
la modélisation du cube. La matérialisation d’un axe n’est rien d’autre qu’un
cube à la forme allongée et dont les cotés ont une longueur égale à une petite
valeur.
La figure 5-14 visualise la modélisation de l’axe au travers d’un cube allongé.
La valeur d représente la longueur de l’axe du côté positif et du côté négatif.
La valeur e représente l’épaisseur des côtés de l’axe.
Figure 5-14

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)

gauche devant droite arrière dessus dessous

0 (-d,-e/2,e/2) 7 (d,-e/2,e/2) 4 (d,-e/2,-e/2) 3 (-d,-e/2,-e/2) 6 (d,e/2,e/2) 0 (-d,-e/2,e/2)


1 (-d,e/2,e/2) 6 (d,e/2,e/2) 5 (d,e/2,-e/2) 2 (-d,e/2,-e/2) 5 (d,e/2,-e/2) 3 (-d,-e/2,-e/2)
2 (-d,e/2,-e/2) 1 (-d,e/2,e/2) 6 (d,e/2,e/2) 5 (d,e/2,-e/2) 2 (-d,e/2,-e/2) 4 (d,-e/2,-e/2)
3 (-d,-e/2,-e/2) 0 (-d,-e/2,e/2) 7 (d,-e/2,e/2) 4 (d,-e/2,-e/2) 1 (-d,e/2,e/2) 7 (d,-e/2,e/2)
CHAPITRE 5 □ Les modèles 3D basiques 145
Figure 5-13
146 La programmation graphique 3D de WPF 4
La démarche consiste à définir la géométrie du cube à la forme allongée. En
fonction de la longueur de l’axe longueur et de l’épaisseur de l’axe epaisseur, on
exprime la position des 8 sommets que l’on affecte à la propriété Positions du
MeshGeometry3D. Ensuite, la méthode AjouterIndicesFace(..) permet d’ajouter
les indices des 2 triangles qui composent une face, à la propriété TriangleIndices
du MeshGeometry3D.

private Geometry3D DefinirGeometrieAxe(double longueur,


double epaisseur) {
MeshGeometry3D maillage = new MeshGeometry3D();
maillage.Positions.Add(
new Point3D(-longueur, -epaisseur / 2, epaisseur / 2));
maillage.Positions.Add(
new Point3D(-longueur, epaisseur / 2, epaisseur / 2));
maillage.Positions.Add(
new Point3D(-longueur, epaisseur / 2, -epaisseur / 2));
maillage.Positions.Add(
new Point3D(-longueur, -epaisseur / 2, -epaisseur / 2));
maillage.Positions.Add(
new Point3D(longueur, -epaisseur / 2, -epaisseur / 2));
maillage.Positions.Add(
new Point3D(longueur, epaisseur / 2, -epaisseur / 2));
maillage.Positions.Add(
new Point3D(longueur, epaisseur / 2, epaisseur / 2));
maillage.Positions.Add(
new Point3D(longueur, -epaisseur / 2, epaisseur / 2));
//face gauche
AjouterIndicesFace(0, 1, 2, 3, maillage.TriangleIndices);
//face devant
AjouterIndicesFace(7, 6, 1, 0, maillage.TriangleIndices);
//face droite
AjouterIndicesFace(4, 5, 6, 7, maillage.TriangleIndices);
//face arriere
AjouterIndicesFace(3, 2, 5, 4, maillage.TriangleIndices);
//face dessus
Copyright 2011 Patrice REY

AjouterIndicesFace(6, 5, 2, 1, maillage.TriangleIndices);
//face dessous
AjouterIndicesFace(0, 3, 4, 7, maillage.TriangleIndices);
return maillage;
}

La méthode AjouterRepereXYZ() permet d’ajouter à la scène 3D un groupe


systeme_axe de type Model3DGroup. L’axe X, instancié par axe_x_pos de type
CHAPITRE 5 □ Les modèles 3D basiques 147
GeometryModel3D, est ajouté au groupe systeme_axe. On définit sa couleur
d’axe au rouge en affectant une couleur Brushes.Red à un objet DiffuseMaterial,
qui est affecté à sa propriété Material. Sa propriété Geometry reçoit la géométrie
de l’axe par la méthode DefinirGeometrieAxe(..), qui prend en paramètre une
longueur d’axe (ici 10 unités) et une épaisseur d’axe (ici 0.05 unités).

private void AjouterRepereXYZ() {


Model3DGroup systeme_axe = new Model3DGroup();
//axe X
GeometryModel3D axe_x_pos = new GeometryModel3D();
axe_x_pos.Material = new DiffuseMaterial(Brushes.Red);
axe_x_pos.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
axe_x_pos.Geometry = DefinirGeometrieAxe(10, 0.005);
systeme_axe.Children.Add(axe_x_pos);
...
//
x_scene_3d.Children.Add(systeme_axe);
}

Les axes Y et Z seront instanciés par axe_y_pos et axe_z_pos, de type


GeometryModel3D. L’axe Y sera colorié en bleu (Brushes.LightSkyBlue) et l’axe
Z sera colorié en vert (Brushes.Green). Une fois que l’axe est instancié, il est
positionné à l’emplacement de l’axe X. Il suffit donc d’effectuer une rotation
de type RotateTransform3D autour d’un axe Z pour l’axe Y et autour d’un axe
X pour l’axe Z, par un objet AxisAngleRotation3D. Cette transformation est
affectée à la propriété Transform de l’objet GeometryModel3D correspondant.

private void AjouterRepereXYZ() {


Model3DGroup systeme_axe = new Model3DGroup();
...
//axe Y
GeometryModel3D axe_y_pos = new GeometryModel3D();
axe_y_pos.Material = new DiffuseMaterial(Brushes.LightSkyBlue);
axe_y_pos.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
axe_y_pos.Geometry = DefinirGeometrieAxe(10, 0.005);
axe_y_pos.Transform = new RotateTransform3D(
new AxisAngleRotation3D(new Vector3D(0, 0, 1), 90));
systeme_axe.Children.Add(axe_y_pos);
//axe Z
GeometryModel3D axe_z_pos = new GeometryModel3D();
axe_z_pos.Material = new DiffuseMaterial(Brushes.Green);
148 La programmation graphique 3D de WPF 4
axe_z_pos.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
axe_z_pos.Geometry = DefinirGeometrieAxe(10, 0.005);
axe_z_pos.Transform = new RotateTransform3D(
new AxisAngleRotation3D(new Vector3D(0, 1, 0), 90));
systeme_axe.Children.Add(axe_z_pos);
//
x_scene_3d.Children.Add(systeme_axe);
}

Les 3 glissières permettent de faire tourner la scène pour une observation


personnalisée. En XAML, la scène est définie comme un Model3DGroup x_
scene_3d.

<!-- conteneur des modeles **************** -->


<Viewport3D.Children>
<ContainerUIElement3D>
<ModelUIElement3D>
<Model3DGroup x:Name='x_scene_3d'>
<!-- groupe: lumiere -->
<Model3DGroup x:Name='x_lumiere'>
<AmbientLight Color='White'></AmbientLight>
</Model3DGroup>
</Model3DGroup>
</ModelUIElement3D>
</ContainerUIElement3D>
</Viewport3D.Children>

La démarche consiste à ajouter au groupe x_scene_3d un groupe de


transformations gp_transform composé des rotations adéquates. Puis d’affecter
gp_transform à la propriété Transform de la scène x_scene_3d.

private void AjouterSceneRotation() {


Transform3DGroup gp_transform = new Transform3DGroup();
m_scene_rotate_x = new RotateTransform3D();
Copyright 2011 Patrice REY

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(..).

//valeur changé sur slider horizontal


private void x_slider_ValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e) {
if (this.IsLoaded == true) {
Slider recup_slider = (Slider)sender;
switch (recup_slider.Name) {
case «x_slider_axe_x»:
m_scene_rotate_x.Rotation = new AxisAngleRotation3D(
new Vector3D(1, 0, 0), x_slider_axe_x.Value);
break;
case «x_slider_axe_y»:
m_scene_rotate_y.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 1, 0), x_slider_axe_y.Value);
break;
case «x_slider_axe_z»:
m_scene_rotate_z.Rotation = new AxisAngleRotation3D(
new Vector3D(0, 0, 1), x_slider_axe_z.Value);
break;
}
AfficherInfosDansGrille();
}
}

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

L’UserControl ExSphere.xaml, dans le dossier chapitre05, visualise une sphère


centrée sur les axes de coordonnées X, Y et Z (figure 5-15). La sphère possède
des faces texturées. Une caméra est positionnée dans l’espace 3D, elle vise
l’origine du repère 3D. Trois glissières horizontales permettent de faire tourner
la scène 3D dans son intégralité autour des axes X, Y et Z.
Une sphère est composée de facettes triangulaires qui sont localisées dans
l’espace 3D et assemblées entre elles. Les sommets des triangles doivent être
localisés par rapport au centre de la sphère.
150 La programmation graphique 3D de WPF 4
Figure 5-15

Copyright 2011 Patrice REY


CHAPITRE 5 □ Les modèles 3D basiques 151
Comme le visualise la figure 5-16, si on considère un point A appartenant à
la sphère, ce point A peut être localisé en fonction du centre O et du rayon r,
avec ses coordonnées qui seront x = r * sin(θ) * cos(φ), y = r * cos(θ) et z= -r *
sin(θ) * sin(φ).

Figure 5-16 Y

r
θ

φ
O X

La méthode ObtenirPosition(..) permet de calculer les coordonnées de la position


d’un point, de type Point3D, en fonction du rayon, de l’angle θ et de l’angle φ.

private Point3D ObtenirPosition(double rayon, double theta,


double phi) {
Point3D pt = new Point3D();
double sinus_theta = Math.Sin(theta * Math.PI / 180);
double cosinus_theta = Math.Cos(theta * Math.PI / 180);
double sinus_phi = Math.Sin(phi * Math.PI / 180);
double cosinus_phi = Math.Cos(phi * Math.PI / 180);
pt.X = rayon * sinus_theta * cosinus_phi;
pt.Y = rayon * cosinus_theta;
pt.Z = -rayon * sinus_theta * sinus_phi;
return pt;
}

La surface de la sphère est divisée en facettes, chaque facette est composée


de deux triangles. Au niveau de l’équateur de la sphère, les facettes sont
152 La programmation graphique 3D de WPF 4
rectangulaires car on divise plus verticalement qu’horizontalement.
Connaissant la façon de calculer la position d’un point dans l’espace 3D, il suffit
de calculer les positions des sommets de la facette en spécifiant arbitrairement
un nombre de divisions en largeur et un nombre de divisions en hauteur. A
partir de quatre sommets qui respectent cette répartition, on peut dessiner
une facette (figure 5-17).
Figure 5-17

( i+1 , j )
(i,j)

( i+1 , j+1 )
( i , j+1 )

Après avoir ajouté le repère XYZ sur la scène, on utilise la méthode


CreerSphere(..) pour réaliser une sphère dont le centre a pour coordonnées
(0,0,0), le rayon a pour valeur 1.5 unités, avec une surface qui est divisée par
20 verticalement et par 15 horizontalement.

//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);
}

On instancie un Model3DGroup gp_sphere qui regroupe toutes les facettes de


la sphère, et on l’ajoute à la scène 3D. Ensuite on détermine tous les points
qui vont servir de support à la réalisation de chaque facette, et cela en
CHAPITRE 5 □ Les modèles 3D basiques 153
fonction du nombre de divisions u verticalement et du nombre de divisions
v horizontalement. Tous ces points, de type Point3D, sont ajoutés dans un
tableau pts à deux dimensions.

private void CreerSphere(Point3D centre, double rayon,


int u, int v) {
if (u < 2 || v < 2) {
return;
}
Model3DGroup gp_sphere = new Model3DGroup();
x_scene_3d.Children.Add(gp_sphere);
Point3D[,] pts = new Point3D[u, v];
for (int i = 0; i < u; i++) {
for (int j = 0; j < v; j++) {
pts[i, j] = ObtenirPosition(rayon,
i * 180 / (u - 1), j * 360 / (v - 1));
pts[i, j] += (Vector3D)centre;
}
}
...
}

On parcoure le tableau dans les deux dimensions et on récupère quatre points


qui sont les sommets d’une facette à dessiner.

private void CreerSphere(Point3D centre, double rayon,


int u, int v) {
...
Point3D[] p = new Point3D[4];
for (int i = 0; i < u - 1; i++) {
for (int j = 0; j < v - 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];
...
}
}
}

Connaissant quatre sommets d’une facette, il suffit alors de générer un


modèle 3D face de type GeometryModel3D. On affecte à ce modèle une surface
154 La programmation graphique 3D de WPF 4
mat_diffuse de type DiffuseMaterial, qui reçoit dans sa propriété Brush un
pinceau image de type ImageBrush. Puis on instancie une géométrie de type
MeshGeometry3D qui va recevoir dans sa propriété Positions les quatre points
d’une facette, dans sa propriété TriangleIndices l’ordre des indices, et dans sa
propriété TextureCoordinates l’ordre des points pour le placage de la texture.
Toutes les facettes sont enfin ajoutées aux enfants de gp_sphere.

private void CreerSphere(Point3D centre, double rayon,


int u, int v) {
...
Point3D[] p = new Point3D[4];
for (int i = 0; i < u - 1; i++) {
for (int j = 0; j < v - 1; j++) {
...
GeometryModel3D face = new GeometryModel3D();
DiffuseMaterial mat_diffuse = new DiffuseMaterial();
ImageBrush img_brush = new ImageBrush();
img_brush.ImageSource = new BitmapImage(
new Uri(«pack://application:,,/contenu/image/
texture-sphere.png», UriKind.Absolute));
mat_diffuse.Brush = img_brush;
face.Material = mat_diffuse;
MeshGeometry3D geometrie = new MeshGeometry3D();
geometrie.Positions.Add(p[0]);
geometrie.Positions.Add(p[1]);
geometrie.Positions.Add(p[2]);
geometrie.Positions.Add(p[3]);
geometrie.TriangleIndices.Add(0);
geometrie.TriangleIndices.Add(1);
geometrie.TriangleIndices.Add(2);
geometrie.TriangleIndices.Add(2);
geometrie.TriangleIndices.Add(3);
geometrie.TriangleIndices.Add(0);
geometrie.TextureCoordinates.Add(new Point(1, 1));
geometrie.TextureCoordinates.Add(new Point(1, 0));
Copyright 2011 Patrice REY

geometrie.TextureCoordinates.Add(new Point(0, 0));


geometrie.TextureCoordinates.Add(new Point(0, 1));
face.Geometry = geometrie;
gp_sphere.Children.Add(face);
}
}
}
CHAPITRE 5 □ Les modèles 3D basiques 155
Le bas de la figure 5-15 montre des copies d’écran dans lesquelles on voit la
scène 3D, composée de la sphère et des 3 axes de coordonnées, après diverses
rotations.

4 - Le cylindre

L’UserControl ExCylindre.xaml, dans le dossier chapitre05, visualise un cylindre


centré sur les axes de coordonnées X, Y et Z (figure 5-18). Le cylindre possède
des faces texturées. Un sélecteur permet de choisir parmi trois types de
cylindre. Une caméra est positionnée dans l’espace 3D, elle vise l’origine du
repère 3D. Trois glissières horizontales permettent de faire tourner la scène
3D dans son intégralité autour des axes X, Y et Z.
Comme le visualise la figure 5-19, le cylindre est composé de facettes qui sont
constituées par deux triangles. Le cylindre peut être caractérisé par une face
cylindrique, côté extérieur, de rayon Rg et une face cylindrique, côté intérieur,
de rayon Rp. De ce fait, la modélisation du cylindre passe par la modélisation
de la face constituée des points A1, A2, B1 et B2, par la modélisation de la
face constituée des points C1, C2, D1 et D2, par la modélisation de la face
constituée des points A1, A2, C1 et C2, et par la modélisation de la face
constituée des points D1, D2, B1 et B2.
La méthode AjouterCylindre(..) permet de modéliser le cylindre en fonction
d’un diamètre, d’une épaisseur de la paroi cylindrique et d’une hauteur du
cylindre.
La démarche consiste à calculer les points qui correspondent aux sommets
des facettes. Quatre listes vont stocker les points de type Point3D. La liste
liste_pt_ext_bas stocke les points de la couronne extérieure du bas du cylindre.
La liste liste_pt_int_bas stocke les points de la couronne intérieure du bas du
cylindre. La liste liste_pt_ext_haut stocke les points de la couronne extérieure
du haut du cylindre. Et la liste liste_pt_int_haut stocke les points de la couronne
intérieure du haut du cylindre. Les points sur les couronnes sont espacés d’un
angle de 10 degrés.

private void AjouterCylindre(double diametre, double epaisseur,


double hauteur) {
Model3DGroup gp_cylindre = new Model3DGroup();
x_cylindres.Children.Add(gp_cylindre);
double ray_ext = diametre / 2;
double ray_int = (diametre - epaisseur) / 2;
156 La programmation graphique 3D de WPF 4
Figure 5-18

Copyright 2011 Patrice REY


CHAPITRE 5 □ Les modèles 3D basiques 157
Figure 5-19

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

List<Point3D> liste_pt_ext_bas = new List<Point3D>();


for (int angle = 0; angle <= 360; angle += 10) {
double x = ray_ext * Math.Cos(-angle * Math.PI / 180);
double y = 0;
double z = -ray_ext * Math.Sin(-angle * Math.PI / 180);
liste_pt_ext_bas.Add(new Point3D(x, y, z));
}
158 La programmation graphique 3D de WPF 4
List<Point3D> liste_pt_ext_haut = new List<Point3D>();
for (int angle = 0; angle <= 360; angle += 10) {
double x = ray_ext * Math.Cos(-angle * Math.PI / 180);
double y = hauteur;
double z = -ray_ext * Math.Sin(-angle * Math.PI / 180);
liste_pt_ext_haut.Add(new Point3D(x, y, z));
}
List<Point3D> liste_pt_int_bas = new List<Point3D>();
for (int angle = 0; angle <= 360; angle += 10) {
double x = ray_int * Math.Cos(-angle * Math.PI / 180);
double y = 0;
double z = -ray_int * Math.Sin(-angle * Math.PI / 180);
liste_pt_int_bas.Add(new Point3D(x, y, z));
}
List<Point3D> liste_pt_int_haut = new List<Point3D>();
for (int angle = 0; angle <= 360; angle += 10) {
double x = ray_int * Math.Cos(-angle * Math.PI / 180);
double y = hauteur;
double z = -ray_int * Math.Sin(-angle * Math.PI / 180);
liste_pt_int_haut.Add(new Point3D(x, y, z));
}
...
}

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).

private void AjouterCylindre(double diametre, double epaisseur,


double hauteur) {
...
//face exterieur
int nb_pt = liste_pt_ext_bas.Count;
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[4];
Copyright 2011 Patrice REY

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);
}
}

Puis les point sont passés à la méthode DessinerFaceCylindrique(..) dans un


ordre précis, en fonction de la face à dessiner et du placage à effectuer (avec
l’utilisation d’un objet GeometryModel3D comme déjà vu).

private void DessinerFaceCylindrique(Point3D pt0, Point3D pt1,


Point3D pt2, Point3D pt3, Model3DGroup mod_gp) {
GeometryModel3D face = new GeometryModel3D();
DiffuseMaterial mat_diffuse = new DiffuseMaterial();
ImageBrush img_brush = new ImageBrush();
img_brush.ImageSource = new BitmapImage(
new Uri(«pack://application:,,/contenu/image/
texture-cylindre.png», UriKind.Absolute));
mat_diffuse.Brush = img_brush;
face.Material = mat_diffuse;
face.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
MeshGeometry3D geometrie = new MeshGeometry3D();
160 La programmation graphique 3D de WPF 4
geometrie.Positions.Add(pt3);
geometrie.Positions.Add(pt2);
geometrie.Positions.Add(pt1);
geometrie.Positions.Add(pt0);
geometrie.TriangleIndices.Add(3);
geometrie.TriangleIndices.Add(2);
geometrie.TriangleIndices.Add(1);
geometrie.TriangleIndices.Add(1);
geometrie.TriangleIndices.Add(0);
geometrie.TriangleIndices.Add(3);
geometrie.TextureCoordinates.Add(new Point(1, 1));
geometrie.TextureCoordinates.Add(new Point(1, 0));
geometrie.TextureCoordinates.Add(new Point(0, 0));
geometrie.TextureCoordinates.Add(new Point(0, 1));
face.Geometry = geometrie;
mod_gp.Children.Add(face);
}

Un sélecteur x_select, de type ComboBox, permet de sélectionner un cylindre


à afficher. La figure 5-18 visualise les différents cylindres générés. Le premier
cylindre est du type anneau et il est généré par la méthode AjouterCylindre(4,
0.5, 0.5). Le deuxième cylindre est du type classique et il est généré par la
méthode AjouterCylindre(3,1,1). Le troisième cylindre est du type plein et il est
généré par la méthode AjouterCylindre(2,2,2).

5 - Le cône

L’UserControl ExCone.xaml, dans le dossier chapitre05, visualise un cône centré


sur les axes de coordonnées X, Y et Z (figure 5-20). Le cône possède des faces
texturées. Un sélecteur permet de choisir parmi six types de cônes. Une
caméra est positionnée dans l’espace 3D, elle vise l’origine du repère 3D.
Trois glissières horizontales permettent de faire tourner la scène 3D dans son
intégralité autour des axes X, Y et Z.
Copyright 2011 Patrice REY

La réalisation d’un cône ressemble à celle d’un cylindre dans la mesure où la


couronne extérieure du haut peut avoir un diamètre nul (cône avec le bout en
pointe) ou bien avoir un diamètre inférieur ou supérieur à la couronne du bas
(cône coupé sur le haut ou sur le bas).
La surface extérieure du cône est composée de facettes qui sont constituées
de deux triangles. Si le nombre de ces facettes est de quatre, nous sommes en
présence alors d’une pyramide.
CHAPITRE 5 □ Les modèles 3D basiques 161
Figure 5-20
162 La programmation graphique 3D de WPF 4
Plus le nombre de facette sera élevé, plus le cône aura une surface extérieure
lisse. La figure 5-21 montre différentes variantes de forme.
Figure 5-21

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.

private void AjouterCone(double dia_base, double dia_pointe,


double hauteur, int division) {
if (division < 4) {
Copyright 2011 Patrice REY

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

double x = rayon_base * Math.Cos(-angle * Math.PI / 180);


double y = 0;
double z = -rayon_base * Math.Sin(-angle * Math.PI / 180);
liste_pt_base.Add(new Point3D(x, y, z));
}
List<Point3D> liste_pt_pointe = new List<Point3D>();
for (int angle = 0; angle <= 360; angle += 360 / division) {
double x = ray_pointe * Math.Cos(-angle * Math.PI / 180);
double y = hauteur;
164 La programmation graphique 3D de WPF 4
double z = -ray_pointe * Math.Sin(-angle * Math.PI / 180);
liste_pt_pointe.Add(new Point3D(x, y, z));
}
...
}

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é.

private void AjouterCone(double dia_base, double dia_pointe,


double hauteur, int division) {
...
//face exterieure
int nb_pt = liste_pt_base.Count;
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[4];
p[0] = liste_pt_base[xx];
p[1] = liste_pt_pointe[xx];
p[2] = liste_pt_pointe[xx + 1];
p[3] = liste_pt_base[xx + 1];
DessinerFaceDeuxTriangles(p[0], p[1], p[2], p[3], gp_cone);
}
//face dessous
for (int xx = 0; xx < nb_pt - 1; xx++) {
Point3D[] p = new Point3D[3];
p[0] = liste_pt_base[xx];
p[1] = liste_pt_base[xx + 1];
p[2] = new Point3D(0, 0, 0);
DessinerFaceUnTriangle(p[2], p[1], p[0], gp_cone);
}
Copyright 2011 Patrice REY

//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

L’UserControl ExTore.xaml, dans le dossier chapitre05, visualise un tore centré


sur les axes de coordonnées X, Y et Z (figure 5-23). Le tore possède des
faces texturées. Un sélecteur permet de choisir parmi 3 types de tores. Une
caméra est positionnée dans l’espace 3D, elle vise l’origine du repère 3D.
Trois glissières horizontales permettent de faire tourner la scène 3D dans son
intégralité autour des axes X, Y et Z.
Le tore désigne un solide géométrique de l’espace euclidien 3D engendré par
la rotation d’un cercle C de rayon r autour d’une droite affine D située dans
son plan à une distance R de son centre.
Si R est inférieur à r, on est en présence d’un tore dit «croisé» (le tore ressemble
visuellement à une citrouille). Si R est égal à r, on est en présence d’un tore dit
«à collier nul». Si R est supérieur à r, on est en présence d’un tore dit «ouvert»
(le tore ressemble à une chambre à air). La figure 5-24 visualise ces données.
La méthode AjouterTore(..) permet de modéliser le tore par rotation d’un cercle
autour d’une droite. Les valeurs N et n permettent de définir, comme pour le
cercle, une largeur et hauteur pour une facette composée de deux triangles.

private void AjouterTore(Point3D center, double R, double r,


int N, int n) {
if (n < 2 || N < 2)
return;
Model3DGroup gp_tore = new Model3DGroup();
Point3D[,] pts = new Point3D[N, n];
for (int i = 0; i < N; i++) {
for (int j = 0; j < n; j++) {
pts[i, j] = ObtenirPosition(R, r, i * 360 / (N - 1),
j * 360 / (n - 1));
pts[i, j] += (Vector3D)center;
}
Copyright 2011 Patrice REY

}
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(β)

DessinerFaceUnTriangle(p[0], p[1], p[2], gp_tore);


DessinerFaceUnTriangle(p[2], p[3], p[0], gp_tore);
}
Copyright 2011 Patrice REY

}
x_tores.Children.Add(gp_tore);
}

La méthode ObtenirPosition(..) permet de définir la position des points en


fonction des paramètres des angles.
CHAPITRE 5 □ Les modèles 3D basiques 169
private Point3D ObtenirPosition(double R, double r,
double u, double v) {
Point3D pt = new Point3D();
double snu = Math.Sin(u * Math.PI / 180);
double cnu = Math.Cos(u * Math.PI / 180);
double snv = Math.Sin(v * Math.PI / 180);
double cnv = Math.Cos(v * Math.PI / 180);
pt.X = (R + r * cnv) * cnu;
pt.Y = r * snv;
pt.Z = -(R + r * cnv) * snu;
return pt;
}

La méthode DessinerFaceUnTriangle(..) permet de dessiner une face de triangle


texturée en fonction de ses trois sommets.

private void DessinerFaceUnTriangle(Point3D pt0, Point3D pt1,


Point3D pt2, Model3DGroup mod_gp) {
GeometryModel3D face = new GeometryModel3D();
DiffuseMaterial mat_diffuse = new DiffuseMaterial();
ImageBrush img_brush = new ImageBrush();
img_brush.ImageSource = new BitmapImage(
new Uri(«pack://application:,,/contenu/image/
texture-tore.png», UriKind.Absolute));
mat_diffuse.Brush = img_brush;
face.Material = mat_diffuse;
face.BackMaterial = new DiffuseMaterial(Brushes.Yellow);
MeshGeometry3D geometrie = new MeshGeometry3D();
geometrie.Positions.Add(pt0);
geometrie.Positions.Add(pt1);
geometrie.Positions.Add(pt2);
geometrie.TriangleIndices.Add(0);
geometrie.TriangleIndices.Add(1);
geometrie.TriangleIndices.Add(2);
geometrie.TextureCoordinates.Add(new Point(1, 1));
geometrie.TextureCoordinates.Add(new Point(1, 0));
geometrie.TextureCoordinates.Add(new Point(0, 0));
geometrie.TextureCoordinates.Add(new Point(0, 1));
face.Geometry = geometrie;
mod_gp.Children.Add(face);
}

Un sélecteur x_select, de type ComboBox, permet de sélectionner un type


170 La programmation graphique 3D de WPF 4
de tore à afficher. La figure 5-23 visualise les différents tores générés. Le
premier tore est un tore classique dit «ouvert» et il est généré par la méthode
AjouterTore(new Point3D(0, 0, 0), 1.6, 0.8, 20, 15). Le deuxième tore est un tore
dit «à collier nul» et il est généré par la méthode AjouterTore(new Point3D(0, 0,
0), 1, 1, 20, 15). Le troisième tore est un tore dit «croisé» et il est généré par la
méthode AjouterTore(new Point3D(0, 0, 0), 0.6, 1.6, 20, 15).

//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;
}
}
}

Copyright 2011 Patrice REY


CHAPITRE 6

Les géométries 3D personnalisées

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.

1 - La géométrie personnalisée du cube

L’UserControl ExGeometrieCube.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant un cube. Il est alors
très facile de réutiliser cette géométrie pour modéliser différents cubes. La
figure 6-1 visualise le résultat obtenu.
La classe GeoCube.cs, dans le dossier chapitre06, gère la personnalisation d’un
modèle représentant un cube. Pour modéliser un cube nous avons besoin
de variables de travail qui sont la largeur m_largeur, la hauteur m_hauteur, la
profondeur m_profondeur et le centre m_centre du cube.

public class GeoCube {


//champs prives de travail
private double m_profondeur;
private double m_largeur;
private double m_hauteur;
private Point3D m_centre;
...
}

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

Copyright 2011 Patrice REY


CHAPITRE 6 □ Les géométries 3D personnalisées 173
//proprietes publiques
public double Profondeur {
get { return m_profondeur; }
set { m_profondeur = value; }
}
public double Largeur {
get { return m_largeur; }
set { m_largeur = value; }
}
public double Hauteur {
get { return m_hauteur; }
set { m_hauteur = 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 cube, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().

//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 GeoCube() {
m_profondeur = 1.0;
m_largeur = 1.0;
m_hauteur = 1.0;
m_centre = new Point3D();
}

La méthode privée ObtenirMaillage3D() définit la géométrie du cube et permet


à la propriété Maillage de retourner cette géométrie en vue de son affichage.
174 La programmation graphique 3D de WPF 4
//definition du maillage
private MeshGeometry3D ObtenirMaillage3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}

La démarche de construction est la même que celle vue au chapitre précédent.


On définit les 8 sommets du cube que l’on stocke dans un tableau pts.

...
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;
}
...

Ensuite on ajoute les sommets dans la propriété Positions du MeshGeometry3D


puis l’ordre des indices des triangles dans la propriété TriangleIndices du
MeshGeometry3D. On répète cette opération pour toutes les faces du cube.
Copyright 2011 Patrice REY

//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);
...

On ajoute les coordonnées de placage dans la propriété TextureCoordinates du


MeshGeometry3D.

//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).

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Centre='0,0,0' Largeur='0.5'
Hauteur='0.5' Profondeur='0.5'></visuel:GeoCube>
</UserControl.Resources>

Commençons par créer un axe de coordonnée X, en rouge, qui a la forme d’un


cube avec une largeur et une hauteur très faibles, mais une profondeur très
importante. On instancie un Model3DGroup intitulé x_axe_x. A l’intérieur, on
ajoute un GeometryModel3D. Sa propriété Geometry est affectée dans le cadre
du Binding par une référence à la ressource statique r_geo_cube (propriété
Source) et une référence à Maillage (propriété Path). On fixe la propriété
Material par un objet Brush de couleur Red. On fixe la propriété Transform par
un TransformGroup composé d’un ScaleTransform3D pour donner au cube une
forme allongée, par les propriétés ScaleX, ScaleY et ScaleZ.

<!-- axe X ************************* -->


<Model3DGroup x:Name='x_axe_x'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource
r_geo_cube},Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial Brush='Red'></DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='20'
ScaleY='0.1'
ScaleZ='0.1'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
Copyright 2011 Patrice REY

</Model3DGroup>

On procède de la même manière pour les axes Y et Z, en appliquant les


transformations adéquates pour la forme et le positionnement, et en affectant
la couleur LightSkyBlue pour l’axe Y et Green pour l’axe Z.
CHAPITRE 6 □ Les géométries 3D personnalisées 177
<!-- axe Y ************************* -->
<Model3DGroup x:Name='x_axe_y'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource
r_geo_cube},Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial Brush='LightSkyBlue'></DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='0.1'
ScaleY='20'
ScaleZ='0.1'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>
<!-- axe Z ************************* -->
<Model3DGroup x:Name='x_axe_z'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource
r_geo_cube},Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial
Brush='Green'></DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='0.1'
ScaleY='0.1' ScaleZ='20'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>

Pour ajouter un cube avec des faces texturées, on procède exactement de la


même manière pour positionner le cube et lui donner une taille particulière.
La seule différence concerne les textures. Pour plaquer une texture, il suffit
d’ajouter un objet ImageBrush, dont la source pointe sur une ressource image,
et d’affecter cet objet à la propriété Brush d’un objet DiffuseMaterial, qui lui-
même est affecté à la propriété Material du GeometryModel3D.
178 La programmation graphique 3D de WPF 4
<Model3DGroup x:Name='x_cube_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource
r_geo_cube},Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cube/texture-cube-01.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<TranslateTransform3D OffsetZ='2'></TranslateTransform3D>
</GeometryModel3D.Transform>
</GeometryModel3D>
</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.

2 - La géométrie personnalisée de la sphère

L’UserControl ExGeometrieSphere.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant une sphère. Il est alors
très facile de réutiliser cette géométrie pour modéliser différentes sphères. La
figure 6-2 visualise le résultat obtenu.
La classe GeoSphere.cs, dans le dossier chapitre06, gère la personnalisation
d’un modèle représentant une sphère. Pour modéliser une sphère nous avons
besoin de variables de travail qui sont la longueur m_longueur_x suivant l’axe
X, la longueur m_longueur_y suivant l’axe Y, la longueur m_longueur_z suivant
l’axe Z, le nombre de division m_division_theta selon l’axe X, le nombre de
Copyright 2011 Patrice REY

division m_division_phi selon l’axe Y, et le centre m_centre de positionnement


de la sphère.

public class GeoSphere {


//champs prives de travail
private double m_longueur_x;
private double m_longueur_y;
CHAPITRE 6 □ Les géométries 3D personnalisées 179
Figure 6-2
180 La programmation graphique 3D de WPF 4
private double m_longueur_z;
private int m_division_theta;
private int m_division_phi;
private Point3D m_centre;
...
}

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();
}

La méthode privée ObtenirMaillage3D() définit la géométrie de la sphère


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;
}

La démarche pour l’obtention du maillage est identique à celle employée


au chapitre 5 pour la modélisation de la sphère. On détermine des facettes
composées de deux triangles. Ces facettes ont une forme de rectangle qui est
fonction de m_division_theta et m_division_phi (plus ces deux facteurs seront
grands, meilleure sera le lissage de la surface de la sphère).

double dt = 360.0 / DivisionTheta;


double dp = 180.0 / DivisionPhi;
MeshGeometry3D mesh = new MeshGeometry3D();
182 La programmation graphique 3D de WPF 4
for (int i = 0; i <= DivisionPhi; i++) {
double phi = i * dp;
for (int j = 0; j <= DivisionTheta; j++) {
double theta = j * dt;
mesh.Positions.Add(ObtenirPosition(theta, phi));
}
}
for (int i = 0; i < DivisionPhi; i++) {
for (int j = 0; j < DivisionTheta; j++) {
int x0 = j;
int x1 = (j + 1);
int y0 = i * (DivisionTheta + 1);
int y1 = (i + 1) * (DivisionTheta + 1);
mesh.TriangleIndices.Add(x0 + y0);
mesh.TriangleIndices.Add(x0 + y1);
mesh.TriangleIndices.Add(x1 + y0);
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));
mesh.TriangleIndices.Add(x1 + y0);
mesh.TriangleIndices.Add(x0 + y1);
mesh.TriangleIndices.Add(x1 + y1);
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));
}
}

La position des points dans l’espace 3D est obtenu par la méthode


ObtenirPosition(..) en fonction des angles fournis.

//position des points en 3d


private Point3D ObtenirPosition(double theta, double phi) {
Copyright 2011 Patrice REY

theta *= Math.PI / 180.0;


phi *= Math.PI / 180.0;
double x = LongueurX * Math.Sin(theta) * Math.Sin(phi);
double y = LongueurY * Math.Cos(phi);
double z = -LongueurZ * Math.Cos(theta) * Math.Sin(phi);
Point3D pt = new Point3D(x, y, z);
pt += (Vector3D)Centre;
return pt;}
CHAPITRE 6 □ Les géométries 3D personnalisées 183
Pour l’utilisation de la classe GeoSphere dans le fichier ExGeometrieSphere.
xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
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_sphere,
qui définit un maillage de base de type sphère avec les propriétés publiques
implémentées et une ressource intitulée r_geo_ellipse, qui définit un maillage
de base de type ellipsoïde.

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Centre='0,0,0' Largeur='0.5'
Hauteur='0.5' Profondeur='0.5'></visuel:GeoCube>
<visuel:GeoSphere x:Key='r_geo_sphere' Centre='0.5,0.5,0.5'
LongueurX='1' LongueurY='1' LongueurZ='1'></visuel:GeoSphere>
<visuel:GeoSphere x:Key='r_geo_ellipse' Centre='0,0,0'
LongueurX='0.5' LongueurY='0.5' LongueurZ='2'></visuel:GeoSphere>
</UserControl.Resources>

Pour ajouter une sphère, on instancie un Model3DGroup intitulé x_sphere_1.


A l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry est
affectée dans le cadre du Binding par une référence à la ressource statique
r_geo_sphere (propriété Source) et une référence à Maillage (propriété Path).
On fixe la propriété Material par un objet Brush dont sa définition sera un
ImageBrush (cas pour ajouter une texture). On fixe la propriété Transform par un
TransformGroup composé des transformations adéquates de positionnement,
de mise à l’échelle et de rotation.

<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>

La figure 6-2 visualise l’ensemble des sphères générées et positionnées, avec


les 3 axes de coordonnées X, Y et Z.

3 - La géométrie personnalisée du cylindre

L’UserControl ExGeometrieCylindre.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant un cylindre. Il
est alors très facile de réutiliser cette géométrie pour modéliser différents
cylindres. La figure 6-3 visualise le résultat obtenu.
La classe GeoCylindre.cs, dans le dossier chapitre06, gère la personnalisation
d’un modèle représentant un cylindre. Pour modéliser un cylindre nous avons
besoin de variables de travail qui sont le rayon interne m_rayon_int, le rayon
externe m_rayon_ext, la hauteur m_hauteur, le nombre de divisions m_division_
theta, et le centre m_centre de positionnement du cylindre.

public class GeoCylindre {


//champs prives
private double m_rayon_int;
private double m_rayon_ext;
private double m_hauteur;
private int m_division_theta;
private Point3D m_centre;
Copyright 2011 Patrice REY

...
}

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().

//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. 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;
}

La démarche pour l’obtention du maillage est identique à celle employée


au chapitre 5 pour la modélisation du cylindre. On détermine des facettes
composées de deux triangles. Ces facettes ont une forme de rectangle qui est
fonction de m_division_theta (plus ce facteur est grand, meilleure sera le lissage
de sa surface).

MeshGeometry3D mesh = new MeshGeometry3D();


if (DivisionTheta < 2 || RayonInterne == RayonExterne) {
return null;
}
double radius = RayonInterne;
if (RayonInterne > RayonExterne) {
RayonInterne = m_rayon_ext;
RayonExterne = radius;
}
double h = Hauteur / 2;
Point3D[,] pts = new Point3D[DivisionTheta, 4];
for (int i = 0; i < DivisionTheta; i++) {
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);
}
for (int i = 0; i < DivisionTheta - 1; i++) {
//surface dessus
188 La programmation graphique 3D de WPF 4
mesh.Positions.Add(pts[i, 0]);
mesh.Positions.Add(pts[i + 1, 0]);
mesh.Positions.Add(pts[i + 1, 3]);
mesh.Positions.Add(pts[i, 3]);
mesh.TriangleIndices.Add(16 * i);
mesh.TriangleIndices.Add(16 * i + 1);
mesh.TriangleIndices.Add(16 * i + 2);
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));
mesh.TriangleIndices.Add(16 * i + 2);
mesh.TriangleIndices.Add(16 * i + 3);
mesh.TriangleIndices.Add(16 * i);
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));
//surface dessous
...
}

Pour l’utilisation de la classe GeoCylindre dans le fichier ExGeometrieCylindre.


xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
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_cylindre,
qui définit un maillage de base de type cylindre avec les propriétés publiques
implémentées, une ressource intitulée r_geo_anneau, qui définit un maillage de
base de type cylindre représentant un anneau, et une ressource intitulée r_geo_
cylindre_plein, qui définit un maillage de base de type cylindre représentant un
cylindre plein.

<!-- ressources usercontrol -->


Copyright 2011 Patrice REY

<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>

Pour ajouter un cylindre, on instancie un Model3DGroup intitulé x_cylindre_1.


A l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry est
affectée dans le cadre du Binding par une référence à la ressource statique
r_geo_cylindre (propriété Source) et une référence à Maillage (propriété Path).
On fixe la propriété Material par un objet Brush dont sa définition sera un
ImageBrush (cas pour ajouter une texture). On fixe la propriété Transform par un
TransformGroup composé des transformations adéquates de positionnement,
de mise à l’échelle et de rotation.

<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>

La figure 6-3 visualise l’ensemble des cylindres générés et positionnés, avec


les 3 axes de coordonnées X, Y et Z.
190 La programmation graphique 3D de WPF 4

4 - La géométrie personnalisée du cône

L’UserControl ExGeometrieCone.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant un cône. Il est alors
très facile de réutiliser cette géométrie pour modéliser différents cônes. La
figure 6-4 visualise le résultat obtenu.
La classe GeoCone.cs, dans le dossier chapitre06, gère la personnalisation d’un
modèle représentant un cône. Pour modéliser un cône nous avons besoin de
variables de travail qui sont le rayon de dessus m_rayon_dessus, le rayon de
dessous m_rayon_dessous, la hauteur m_hauteur, le nombre de divisions m_
division_theta, et le centre m_centre de positionnement du cône.

public class GeoCone {


//champs prives
private double m_rayon_dessus;
private double m_rayon_dessous;
private double m_hauteur;
private int m_division_theta;
private Point3D m_centre;
...
}

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.

//proprietes publiques (lecture ecriture)


public double RayonDessus {
get { return m_rayon_dessus; }
set { m_rayon_dessus = value; }
}
public double RayonDessous {
get { return m_rayon_dessous; }
Copyright 2011 Patrice REY

set { m_rayon_dessous = value; }


}
public double Hauteur {
get { return m_hauteur; }
set { m_hauteur = value; }
}
public int DivisionTheta {
CHAPITRE 6 □ Les géométries 3D personnalisées 191
Figure 6-4
192 La programmation graphique 3D de WPF 4
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 cône, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().

//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 GeoCone() {
m_rayon_dessus = 1.0;
m_rayon_dessous = 1.0;
m_hauteur = 1.0;
m_division_theta = 20;
m_centre = new Point3D();
}

La méthode privée ObtenirMaillage3D() définit la géométrie du cône et permet


à la propriété Maillage d’obtenir cette géométrie en vue de son affichage.

//definition du maillage
Copyright 2011 Patrice REY

private MeshGeometry3D ObtenirMaillage3D() {


MeshGeometry3D mesh = new MeshGeometry3D();
...
//empeche la modification
mesh.Freeze();
return mesh;
}
CHAPITRE 6 □ Les géométries 3D personnalisées 193
La démarche pour l’obtention du maillage est identique à celle employée au
chapitre 5 pour la modélisation du cône. On détermine des facettes composées
de deux triangles. Ces facettes ont une forme de rectangle qui est fonction
de m_division_theta (plus ce facteur est grand, meilleure sera le lissage de sa
surface).

MeshGeometry3D mesh = new MeshGeometry3D();


if (DivisionTheta < 2) {
return null;
}
double h = Hauteur / 2;
Point3D pt = new Point3D(0, h, 0);
pt += (Vector3D)Centre;
Point3D pb = new Point3D(0, -h, 0);
pb += (Vector3D)Centre;
Point3D[] pts = new Point3D[DivisionTheta];
Point3D[] pbs = new Point3D[DivisionTheta];
for (int i = 0; i < DivisionTheta; i++) {
pts[i] = ObtenirPosition(RayonDessus,
i * 360 / (DivisionTheta - 1), h);
pbs[i] = ObtenirPosition(RayonDessous,
i * 360 / (DivisionTheta - 1), -h);
}
for (int i = 0; i < DivisionTheta - 1; i++) {
//surface dessus
mesh.Positions.Add(pt);
mesh.Positions.Add(pts[i]);
mesh.Positions.Add(pts[i + 1]);
mesh.TriangleIndices.Add(10 * i);
mesh.TriangleIndices.Add(10 * i + 1);
mesh.TriangleIndices.Add(10 * i + 2);
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));
//surface dessous
mesh.Positions.Add(pb);
mesh.Positions.Add(pbs[i + 1]);
mesh.Positions.Add(pbs[i]);
mesh.TriangleIndices.Add(10 * i + 3);
mesh.TriangleIndices.Add(10 * i + 4);
mesh.TriangleIndices.Add(10 * i + 5);
mesh.TextureCoordinates.Add(new Point(1, 0));
194 La programmation graphique 3D de WPF 4
mesh.TextureCoordinates.Add(new Point(1, 1));
mesh.TextureCoordinates.Add(new Point(0, 1));
mesh.TextureCoordinates.Add(new Point(0, 0));
//surface exterieure
mesh.Positions.Add(pts[i]);
mesh.Positions.Add(pbs[i]);
mesh.Positions.Add(pbs[i + 1]);
mesh.Positions.Add(pts[i + 1]);
mesh.TriangleIndices.Add(10 * i + 6);
mesh.TriangleIndices.Add(10 * i + 7);
mesh.TriangleIndices.Add(10 * i + 8);
mesh.TriangleIndices.Add(10 * i + 8);
mesh.TriangleIndices.Add(10 * i + 9);
mesh.TriangleIndices.Add(10 * i + 6);
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));
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));
}

Pour l’utilisation de la classe GeoCone dans le fichier ExGeometrieCone.


xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
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_cone_
classique, qui définit un maillage de base de type cône avec les propriétés
publiques implémentées, une ressource intitulée r_geo_cone_coupe, qui définit
un maillage de base de type cône avec la pointe coupée, une ressource intitulée
r_geo_pyramide, qui définit un maillage de base de type cône représentant
une pyramide, et une ressource intitulée r_geo_pyramide_coupe, qui définit
Copyright 2011 Patrice REY

un maillage de base de type cône représentant une pyramide avec la pointe


coupée.

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Largeur='1' Hauteur='1'
Profondeur='1' Centre='0,0,0'></visuel:GeoCube>
CHAPITRE 6 □ Les géométries 3D personnalisées 195
<visuel:GeoCone x:Key='r_geo_cone_classique' Centre='0,0,0'
RayonDessus='0' RayonDessous='1' Hauteur='1'></visuel:GeoCone>
<visuel:GeoCone x:Key='r_geo_cone_coupe' Centre='0,0,0'
RayonDessus='0.5' RayonDessous='1' Hauteur='1'></visuel:GeoCone>
<visuel:GeoCone x:Key='r_geo_pyramide' Centre='0,0,0'
RayonDessus='0' RayonDessous='1' Hauteur='1'
DivisionTheta='4'></visuel:GeoCone>
<visuel:GeoCone x:Key='r_geo_pyramide_coupe' Centre='0,0,0'
RayonDessus='0.5' RayonDessous='1' Hauteur='1'
DivisionTheta='4'></visuel:GeoCone>
</UserControl.Resources>

Pour ajouter un cône, on instancie un Model3DGroup intitulé x_cone_1. A


l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry est affectée
dans le cadre du Binding par une référence à la ressource statique r_geo_
cone_classique (propriété Source) et une référence à Maillage (propriété Path).
On fixe la propriété Material par un objet Brush dont sa définition sera un
ImageBrush (cas pour ajouter une texture). On fixe la propriété Transform par un
TransformGroup composé des transformations adéquates de positionnement,
de mise à l’échelle et de rotation.

<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.

5 - La géométrie personnalisée du tore

L’UserControl ExGeometrieTore.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant un tore. Il est alors
très facile de réutiliser cette géométrie pour modéliser différents tores. La
figure 6-5 visualise le résultat obtenu.
La classe GeoTore.cs, dans le dossier chapitre06, gère la personnalisation d’un
modèle représentant un tore. Pour modéliser un tore nous avons besoin de
variables de travail qui sont le rayon du grand cercle m_rayon_1, le rayon du
petit cercle m_rayon_2, le nombre de divisions m_division_h horizontalement,
le nombre de divisions m_division_v verticalement, et le centre m_centre de
positionnement du tore.

public class GeoTore {


//champs prives
private double m_rayon_1;
private double m_rayon_2;
private int m_division_h;
private int m_division_v;
private Point3D m_centre;
...
}

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.

//proprietes publiques (lecture ecriture)


public double Rayon1 {
get { return m_rayon_1; }
Copyright 2011 Patrice REY

set { m_rayon_1 = value; }


}
public double Rayon2 {
get { return m_rayon_2; }
set { m_rayon_2 = value; }
}
public int DivisionH {
get { return m_division_h; }
CHAPITRE 6 □ Les géométries 3D personnalisées 197
Figure 6-5
198 La programmation graphique 3D de WPF 4
set { m_division_h = value; }
}
public int DivisionV {
get { return m_division_v; }
set { m_division_v = 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 tore, en retournant cette géométrie par l’appel de la méthode
ObtenirMaillage3D().

//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 GeoTore() {
m_rayon_1 = 1.0;
m_rayon_2 = 0.3;
m_division_h = 20;
m_division_v = 20;
m_centre = new Point3D();
}

La méthode privée ObtenirMaillage3D() définit la géométrie du tore et permet


Copyright 2011 Patrice REY

à 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();
CHAPITRE 6 □ Les géométries 3D personnalisées 199
return mesh;
}

La démarche pour l’obtention du maillage est identique à celle employée au


chapitre 5 pour la modélisation du tore. On détermine des facettes composées
de deux triangles. Ces facettes ont une forme de rectangle qui est fonction de
m_division_h et m_division_v.

if (DivisionH < 2 || DivisionV < 2) {


return null;
}
MeshGeometry3D mesh = new MeshGeometry3D();
Point3D[,] pts = new Point3D[DivisionH, DivisionV];
for (int i = 0; i <= DivisionH; i++) {
for (int j = 0; j <= DivisionV; j++) {
mesh.Positions.Add(GetPosition(i * 360 / (DivisionH - 1),
j * 360 / (DivisionV - 1)));
}
}
for (int i = 0; i < DivisionH; i++) {
for (int j = 0; j < DivisionV; j++) {
int y0 = j;
int y1 = j + 1;
int x0 = i * (DivisionV + 1);
int x1 = (i + 1) * (DivisionV + 1);
mesh.TriangleIndices.Add(x0 + y0);
mesh.TriangleIndices.Add(x0 + y1);
mesh.TriangleIndices.Add(x1 + y0);
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));
mesh.TriangleIndices.Add(x1 + y0);
mesh.TriangleIndices.Add(x0 + y1);
mesh.TriangleIndices.Add(x1 + y1);
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));
}
}

Pour l’utilisation de la classe GeoTore dans le fichier ExGeometrieTore.


200 La programmation graphique 3D de WPF 4
xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
De façon à réutiliser plusieurs fois le maillage, on ajoute dans les ressources
de l’UserControl (<UserControl.Resources>) les ressources intitulées r_geo_tore_1,
r_geo_tore_2, r_geo_tore_3, r_geo_tore_4 et r_geo_tore_5, qui définissent un
maillage de base de type tore avec différentes variantes.

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Largeur='1' Hauteur='1'
Profondeur='1' Centre='0,0,0'></visuel:GeoCube>
<visuel:GeoTore x:Key='r_geo_tore_1' Centre='0,0,0' Rayon1='1.5'
Rayon2='0.2' DivisionV='30'></visuel:GeoTore>
<visuel:GeoTore x:Key='r_geo_tore_2' Centre='0,0,0' Rayon1='0.5'
Rayon2='0.4' DivisionH='50' DivisionV='50'></visuel:GeoTore>
<visuel:GeoTore x:Key='r_geo_tore_3' Centre='0,0,0' Rayon1='0.3'
Rayon2='0.5' DivisionH='10' DivisionV='10'></visuel:GeoTore>
<visuel:GeoTore x:Key='r_geo_tore_4' Centre='0,0,0' Rayon1='0'
Rayon2='0.7' DivisionH='50' DivisionV='50'></visuel:GeoTore>
<visuel:GeoTore x:Key='r_geo_tore_5' Centre='0,0,0' Rayon1='0.5'
Rayon2='0.5' DivisionH='6' DivisionV='6'></visuel:GeoTore>
</UserControl.Resources>

Pour ajouter un tore, on instancie un Model3DGroup intitulé x_tore_1. A


l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry est affectée
dans le cadre du Binding par une référence à la ressource statique r_geo_tore_1
(propriété Source) et une référence à Maillage (propriété Path). On fixe la
propriété Material par un objet Brush dont sa définition sera un ImageBrush (cas
pour ajouter une texture). On fixe la propriété Transform par un TransformGroup
composé des transformations adéquates de positionnement, de mise à l’échelle
et de rotation.

<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.

6 - La géométrie personnalisée de l’icosaèdre

L’UserControl ExGeometrieIcosaedre.xaml, dans le dossier chapitre06, montre


l’utilisation d’une géométrie personnalisée, représentant un icosaèdre. Il
est alors très facile de réutiliser cette géométrie pour modéliser différents
icosaèdres. La figure 6-6 visualise le résultat obtenu.
L’icosaèdre est un polyèdre régulier convexe (appartenant à la famille des solides
de Platon) qui possède 20 faces représentées par des triangles équilatéraux.
Cinq faces se rencontrent en chaque sommet. L’icosaèdre possède 20 faces, 12
sommets et 30 arêtes. C’est un solide de dimension trois, avec sa surface qui
est composée d’un nombre fini de polygones. La figure 6-7 visualise sa forme
et indique la façon d’obtenir les coordonnées de ses 12 sommets en fonction
du nombre d’or phi.
La classe GeoIcosaedre.cs, dans le dossier chapitre06, gère la personnalisation
d’un modèle représentant un icosaèdre. Pour modéliser un icosaèdre nous
avons besoin de variables de travail qui sont la longueur d’un côté m_longueur_
cote, et le centre m_centre de positionnement de l’icosaèdre.

public class GeoIcosaedre {


//champs prives
private double m_longueur_cote;
private Point3D m_centre;
...}
202 La programmation graphique 3D de WPF 4
Figure 6-6

Copyright 2011 Patrice REY


CHAPITRE 6 □ Les géométries 3D personnalisées 203
Figure 6-7

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

Ces variables servent à définir les propriétés publiques de l’icosaèdre (en


lecture et écriture) qui sont LongueurCote et Centre.

//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().

//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
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();
}

La méthode privée ObtenirMaillage3D() définit la géométrie de l’icosaèdre


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;
}

La démarche, pour l’obtention du maillage, consiste à définir les 20 triangles


équilatéraux en fonction des 12 sommets, et d’appliquer une texture sur
chaque triangle.

MeshGeometry3D mesh = new MeshGeometry3D();


Point3D[] pts = new Point3D[12];
double phi = 0.5 * LongueurCote * (1 + Math.Sqrt(5.0));
pts[0] = new Point3D(LongueurCote, 0, phi);
pts[1] = new Point3D(-LongueurCote, 0, phi);
pts[2] = new Point3D(0, phi, LongueurCote);
pts[3] = new Point3D(0, -phi, LongueurCote);
pts[4] = new Point3D(phi, LongueurCote, 0);
pts[5] = new Point3D(-phi, LongueurCote, 0);
Copyright 2011 Patrice REY

pts[6] = new Point3D(-phi, -LongueurCote, 0);


pts[7] = new Point3D(phi, -LongueurCote, 0);
pts[8] = new Point3D(0, phi, -LongueurCote);
pts[9] = new Point3D(0, -phi, -LongueurCote);
pts[10] = new Point3D(LongueurCote, 0, -phi);
pts[11] = new Point3D(-LongueurCote, 0, -phi);
for (int i = 0; i < 12; i++) {
CHAPITRE 6 □ Les géométries 3D personnalisées 205
pts[i] += (Vector3D)Centre;
}
// Face1:
mesh.Positions.Add(pts[0]);
mesh.Positions.Add(pts[1]);
mesh.Positions.Add(pts[3]);
// Face2:
mesh.Positions.Add(pts[0]);
mesh.Positions.Add(pts[2]);
mesh.Positions.Add(pts[1]);
// Face3:
...
for (int i = 0; i < 60; i++) {
mesh.TriangleIndices.Add(i);
}
//
for (int i = 0; i < 20; i++) {
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));
}

Pour l’utilisation de la classe GeoIcosaedre dans le fichier ExGeometrieIcosaedre.


xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
De façon à réutiliser plusieurs fois le maillage, on ajoute dans les ressources
de l’UserControl (<UserControl.Resources>) la ressource intitulée r_geo_icosaedre,
qui définit un maillage de base de type icosaèdre.

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Largeur='1' Hauteur='1'
Profondeur='1' Centre='0,0,0'></visuel:GeoCube>
<visuel:GeoIcosaedre x:Key='r_geo_icosaedre' Centre='0,0,0'
LongueurCote='0.5'></visuel:GeoIcosaedre>
</UserControl.Resources>

Pour ajouter un icosaèdre, on instancie un Model3DGroup intitulé x_icosaedre_1.


A l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry est
affectée dans le cadre du Binding par une référence à la ressource statique
r_geo_icosaedre (propriété Source) et une référence à Maillage (propriété Path).
206 La programmation graphique 3D de WPF 4
On fixe la propriété Material par un objet Brush dont sa définition sera un
ImageBrush (cas pour ajouter une texture). On fixe la propriété Transform par un
TransformGroup composé des transformations adéquates de positionnement,
de mise à l’échelle et de rotation.

<!-- geometrie icosaedre ******************************* -->


<Model3DGroup x:Name='x_icosaedre_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_icosaedre},
Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cube/texture-cube-01.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.Transform>
<Transform3DGroup>
<TranslateTransform3D
OffsetZ='2'></TranslateTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>

La figure 6-6 visualise l’ensemble des icosaèdres générés et positionnés, avec


les 3 axes de coordonnées X, Y et Z.

7 - La géométrie personnalisée du dodécaèdre

L’UserControl ExGeometrieDodecaedre.xaml, dans le dossier chapitre06, montre


Copyright 2011 Patrice REY

l’utilisation d’une géométrie personnalisée, représentant un dodécaèdre. Il


est alors très facile de réutiliser cette géométrie pour modéliser différents
dodécaèdres. La figure 6-8 visualise le résultat obtenu.
Le dodécaèdre est un polyèdre régulier convexe (appartenant à la famille des
solides de Platon) qui possède 12 faces représentées par des pentagones. Trois
faces se rencontrent en chaque sommet. Le dodécaèdre possède 12 faces, 20
sommets et 30 arêtes.
CHAPITRE 6 □ Les géométries 3D personnalisées 207
Figure 6-8
208 La programmation graphique 3D de WPF 4
C’est un solide de dimension trois, avec sa surface qui est composée d’un
nombre fini de pentagones. La figure 6-9 visualise sa forme et indique la façon
d’obtenir les coordonnées de ses 20 sommets en fonction du nombre d’or phi.
Figure 6-9 10 phi = 0.5 * (1 + Math.Sqrt(5.0))
phi1 = 1.0 / phi
11
phi *= LongueurCote
9 phi1 *= LongueurCote
2
(0, phi1, phi)
19 12 (0, -phi1, phi)
18 (LongueurCote, LongueurCote, LongueurCote)
(-LongueurCote, LongueurCote, LongueurCote)
8 3 ( -LongueurCote, -LongueurCote, LongueurCote)
1 (LongueurCote, -LongueurCote, LongueurCote)
20 (phi, 0, phi1)
17 (-phi, 0, phi1)
13 (phi1, phi, 0)
16 (-phi1, phi, 0)
7
(-phi1, -phi, 0)
5 4 (phi1, -phi, 0)
(phi, 0, -phi1)
(-phi, 0, -phi1)
14 (LongueurCote, LongueurCote, -LongueurCote)
6 15 (-LongueurCote, LongueurCote, -LongueurCote)
(-LongueurCote, -LongueurCote, -LongueurCote)
(LongueurCote, -LongueurCote, -LongueurCote)
(0, phi1, -phi)
(0, -phi1, -phi)

La classe GeoDodecaedre.cs, dans le dossier chapitre06, gère la personnalisation


d’un modèle représentant un dodécaèdre. Pour modéliser un dodécaèdre nous
avons besoin de variables de travail qui sont la longueur d’un côté m_longueur_
cote, et le centre m_centre de positionnement du dodécaèdre.

public class GeoDodecaedre {


//champs prives
private double m_longueur_cote;
private Point3D m_centre;
...
Copyright 2011 Patrice REY

Ces variables servent à définir les propriétés publiques du dodécaèdre (en


lecture et en écriture) qui sont LongueurCote et Centre.

//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().

//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 GeoDodecaedre() {
m_longueur_cote = 1.0;
m_centre = new Point3D();
}

La méthode privée ObtenirMaillage3D() définit la géométrie du dodécaèdre


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;
}

La démarche pour l’obtention du maillage consiste à définir les 12 pentagones


210 La programmation graphique 3D de WPF 4
en fonction des 20 sommets, et d’appliquer une texture sur chaque pentagone.

MeshGeometry3D mesh = new MeshGeometry3D();


Point3D[] pts = new Point3D[20];
double phi = 0.5 * (1 + Math.Sqrt(5.0));
double phi1 = 1.0 / phi;
phi *= LongueurCote;
phi1 *= LongueurCote;
pts[0] = new Point3D(0, phi1, phi);
pts[1] = new Point3D(0, -phi1, phi);
pts[2] = new Point3D(LongueurCote, LongueurCote, LongueurCote);
pts[3] = new Point3D(-LongueurCote, LongueurCote, LongueurCote);
pts[4] = new Point3D(-LongueurCote, -LongueurCote, LongueurCote);
pts[5] = new Point3D(LongueurCote, -LongueurCote, LongueurCote);
pts[6] = new Point3D(phi, 0, phi1);
pts[7] = new Point3D(-phi, 0, phi1);
pts[8] = new Point3D(phi1, phi, 0);
pts[9] = new Point3D(-phi1, phi, 0);
pts[10] = new Point3D(-phi1, -phi, 0);
pts[11] = new Point3D(phi1, -phi, 0);
pts[12] = new Point3D(phi, 0, -phi1);
pts[13] = new Point3D(-phi, 0, -phi1);
pts[14] = new Point3D(LongueurCote, LongueurCote, -LongueurCote);
pts[15] = new Point3D(-LongueurCote, LongueurCote, -LongueurCote);
pts[16] = new Point3D(-LongueurCote, -LongueurCote, -LongueurCote);
pts[17] = new Point3D(LongueurCote, -LongueurCote, -LongueurCote);
pts[18] = new Point3D(0, phi1, -phi);
pts[19] = new Point3D(0, -phi1, -phi);
for (int i = 0; i < 20; i++) {
pts[i] += (Vector3D)Centre;
}
// Face 1 (0,1,5,6,2):
mesh.Positions.Add(pts[0]);
mesh.Positions.Add(pts[1]);
mesh.Positions.Add(pts[2]);
mesh.Positions.Add(pts[5]);
Copyright 2011 Patrice REY

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):
...

Pour l’utilisation de la classe GeoDodecaedre dans le fichier ExGeometrieDodecaedre.


xaml, il faut ajouter une référence à l’espace de noms xmlns:visuel = «clr-
namespace:Prog3dWpf4.chapitre06» que l’on préfixe par visuel.
De façon à réutiliser plusieurs fois le maillage, on ajoute dans les ressources de
l’UserControl (<UserControl.Resources>) la ressource intitulée r_geo_dodecaedre,
qui définit un maillage de base de type dodécaèdre.

<!-- ressources usercontrol -->


<UserControl.Resources>
<visuel:GeoCube x:Key='r_geo_cube' Largeur='1' Hauteur='1'
Profondeur='1' Centre='0,0,0'></visuel:GeoCube>
<visuel:GeoDodecaedre x:Key='r_geo_dodecaedre' Centre='0,0,0'
LongueurCote='1'></visuel:GeoDodecaedre>
</UserControl.Resources>

Pour ajouter un dodécaèdre, on instancie un Model3DGroup intitulé x_


dodecaedre_1. A l’intérieur, on ajoute un GeometryModel3D. Sa propriété Geometry
est affectée dans le cadre du Binding par une référence à la ressource statique
r_geo_dodecaedre (propriété Source) et une référence à Maillage (propriété Path).
On fixe la propriété Material par un objet Brush dont sa définition sera un
ImageBrush (cas pour ajouter une texture). On fixe la propriété Transform par un
TransformGroup composé des transformations adéquates de positionnement,
de mise à l’échelle et de rotation.

<!-- geometrie dodecaedre ******************************* -->


<Model3DGroup x:Name='x_dodecaedre_1'>
<GeometryModel3D
Geometry='{Binding Source={StaticResource r_geo_
dodecaedre},Path=Maillage}'>
<GeometryModel3D.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/image/
texture-dodecaedre.png'></ImageBrush>
212 La programmation graphique 3D de WPF 4
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</GeometryModel3D.Material>
<GeometryModel3D.BackMaterial>
<DiffuseMaterial Brush='Yellow'></DiffuseMaterial>
</GeometryModel3D.BackMaterial>
<GeometryModel3D.Transform>
<Transform3DGroup>
<ScaleTransform3D ScaleX='1'
ScaleY='1'
ScaleZ='1'></ScaleTransform3D>
</Transform3DGroup>
</GeometryModel3D.Transform>
</GeometryModel3D>
</Model3DGroup>

La figure 6-8 visualise le dodécaèdre généré et positionné, avec les 3 axes de


coordonnées X, Y et Z.

Copyright 2011 Patrice REY


CHAPITRE 7

L’interactivité des modèles 3D

La création de modèles 3D est passionnante, mais faire en sorte que votre


objet 3D réalisé puisse réagir aux entrées clavier, au focus et aux événements
est encore plus attrayant.
Dans ce chapitre, nous allons voir comment ajouter de l’interactivité dans la
création et la manipulation des objets 3D.

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.

public class UICylindre : UIElement3D {


protected override void OnUpdateModel() {
GeometryModel3D mon_modele = new GeometryModel3D();
Modele = mon_modele;
UIGeoCylindre geometry = new UIGeoCylindre();
geometry.RayonInterne = RayonInterne;
Copyright 2011 Patrice REY

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.

//la propriete de dependance de UICylindre: RayonInterne


public static readonly DependencyProperty RayonInternePropriete =
DependencyProperty.Register(«RayonInterne»,
typeof(double),
typeof(UICylindre),
new PropertyMetadata(0.0, ProprieteModifiee));
public double RayonInterne {
get { return (double)GetValue(RayonInternePropriete); }
set { SetValue(RayonInternePropriete, value); }
Copyright 2011 Patrice REY

La méthode statique ProprieteModifiee(..) consiste à appliquer sur le UICylindre


la méthode InvalidateModel() qui invalide le modèle qui représente l’élément
de façon à le redessiner.
CHAPITRE 7 □ L’interactivité des modèles 3D 217
private static void ProprieteModifiee(DependencyObject d,
DependencyPropertyChangedEventArgs e) {
UICylindre cylindre = (UICylindre)d;
cylindre.InvalidateModel();
}

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).

//la propriete de dependance de UICylindre: Centre


public static readonly DependencyProperty CentrePropriete =
DependencyProperty.Register(«Centre»,
typeof(Point3D),
typeof(UICylindre),
new PropertyMetadata(new Point3D(0,0,0), ProprieteModifiee));
public Point3D Centre {
get { return (Point3D)GetValue(CentrePropriete); }
set { SetValue(CentrePropriete, value); }
}

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).

//la propriete de dependance de UICylindre: DivisionTheta


public static readonly DependencyProperty DivisionThetaPropriete =
DependencyProperty.Register(«DivisionTheta»,
typeof(int),
typeof(UICylindre),
new PropertyMetadata(20, ProprieteModifiee));
public int DivisionTheta {
get { return (int)GetValue(DivisionThetaPropriete); }
set { SetValue(DivisionThetaPropriete, value); }
}

Pour la propriété de dépendance Material, l’initialisation sera faite avec un


pinceau de couleur par défaut ( par new DiffuseMaterial( Brushes.LightSteelBlue )
pour un bleu). Cela n’empêchera pas d’ajouter une texture par la suite.
218 La programmation graphique 3D de WPF 4
//la propriete de dependance de UICylindre: Material
public static readonly DependencyProperty MaterialPropriete =
DependencyProperty.Register(«Material»,
typeof(Material),
typeof(UICylindre),
new PropertyMetadata(new DiffuseMaterial(
Brushes.LightSteelBlue), ProprieteModifiee));
public Material Material {
get { return (Material)GetValue(MaterialPropriete); }
set { SetValue(MaterialPropriete, value); }
}

La propriété de dépendance Modele sera déclarée de la même façon. Mais la


méthode statique invoquée sera ModeleProprieteModifiee() de façon à modifier
la propriété Model3D de Visual3DModel.

//la propriete de dependance de UICylindre: Model


private static readonly DependencyProperty ModelePropriete =
DependencyProperty.Register(«Modele»,
typeof(Model3D),
typeof(UICylindre),
new PropertyMetadata(ModeleProprieteModifiee));
private Model3D Modele {
get { return (Model3D)GetValue(ModelePropriete); }
set { SetValue(ModelePropriete, value); }
}
private static void ModeleProprieteModifiee(DependencyObject d,
DependencyPropertyChangedEventArgs e) {
UICylindre cylindre = (UICylindre)d;
cylindre.Visual3DModel = (Model3D)e.NewValue;
}

La partie XAML se trouve alors beaucoup plus allégée. On nomme x_scene_3d


l’objet ContainerUIElement3D. Dans un objet ModelUIElement3D, on affecte un
Copyright 2011 Patrice REY

Model3DGroup pour y placer les lumières (x_lumiere).


Pour positionner le cylindre, on ajoute une référence à l’espace de noms
xmlns:obj = «clr-namespace:Prog3dWpf4.chapitre07», préfixé par obj, et on ajoute
le cylindre par <obj:UICylindre ...> en fixant ses propriétés si nécessaire.
Pour ajouter une texture, on redéfinit la propriété Brush de DiffuseMaterial de
UICylindre.
CHAPITRE 7 □ L’interactivité des modèles 3D 219
<Viewport3D.Children>
<ContainerUIElement3D x:Name='x_scene_3d'>
<ModelUIElement3D>
<Model3DGroup x:Name='x_lumiere'>
<AmbientLight></AmbientLight>
</Model3DGroup>
</ModelUIElement3D>
...
<!-- cylindre -->
<obj:UICylindre x:Name='x_cylindre' RayonExterne='2'
RayonInterne='1'
MouseLeftButtonDown='x_cylindre_MouseLeftButtonDown'>
<obj:UICylindre.Material>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cylindre/texture-cylindre-04.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICylindre.Material>
</obj:UICylindre>
</ContainerUIElement3D>
</Viewport3D.Children>

A partir de là, l’élément est fonctionnel et et il est doté de la gestion des


événements (et autres diverses entrées). Nous ajoutons un gestionnaire
d’événement pour gérer le clic gauche enfoncé sur le cylindre. Cet événement
provoque l’affichage d’un objet MessageBox dans lequel les caractéristiques
du cylindre sont explicitées (le bas de la figure 7-1 montre une copie d’écran).

//clic sur le cylindre


private void x_cylindre_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e) {
UICylindre le_cylindre = (UICylindre)sender;
string message = «»;
message += «rayon externe = « + le_cylindre.RayonExterne.ToString()
+ « unités» + RC;
message += «rayon interne = « + le_cylindre.RayonInterne.ToString()
+ « unités» + RC;
message += «hauteur = « + le_cylindre.Hauteur.ToString()
+ « unités» + RC;
220 La programmation graphique 3D de WPF 4
message += «division theta = « +
(le_cylindre.DivisionTheta - 1).ToString() + « divisions» + RC;
MessageBox.Show(message);
}

Pour pouvoir modifier à la volée les propriétés du cylindre, on ajoute un


ensemble de glissières, de type Slider. Par le procédé du Binding (comme on
l’a déjà vu), on relie les glissières aux propriétés RayonInterne, RayonExterne,
Hauteur et DivisionTheta. La figure 7-1 visualise différentes formes obtenues en
modifiant les propriétés du cylindre.

<Label Margin='0' Grid.Row='0' Name='label1'>rayon interne</Label>


<Slider Grid.Column='1' Name='slider1'
Value='{Binding ElementName=x_cylindre, Path=RayonInterne}'
Minimum='0' Maximum='1.5' />
<Label Margin='0' Grid.Row='1' Name='label2'>Rayon externe</Label>
<Slider Grid.Row='1' Grid.Column='1' Name='slider2' Margin='0'
Value='{Binding ElementName=x_cylindre, Path=RayonExterne}'
Minimum='1.6' Maximum='3' />
<Label Margin='0' Grid.Row='2' Name='label3'>hauteur</Label>
<Slider Grid.Row='2' Grid.Column='1' Name='slider3' Margin='0'
Value='{Binding ElementName=x_cylindre, Path=Hauteur}'
Minimum='0.1' Maximum='5' />
<Label Margin='0' Grid.Row='3' Name='label4'>division theta</Label>
<Slider Grid.Row='3' Grid.Column='1' Name='slider4' Margin='0'
Value='{Binding ElementName=x_cylindre, Path=DivisionTheta}'
Minimum='4' Maximum='50' />

2 - Combinaison de modèles

L’UserControl ExUIElementCombinaison.xaml, dans le dossier chapitre07, permet


de visualiser un contrôle, qui hérite de UIElement3D, qui est composé de
Copyright 2011 Patrice REY

plusieurs modèles pour en former un seul, de façon à lui ajouter de l’interactivité


(figure 7-2). 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, souris, etc.) et
aux événements dans l’espace 3D, tout comme le ferait un contrôle 2D dans
l’espace 2D.
CHAPITRE 7 □ L’interactivité des modèles 3D 221
Figure 7-2
222 La programmation graphique 3D de WPF 4
La classe UICombinaison représente une combinaison de modèles, et elle
hérite de UIElement3D. Elle est composée d’un groupe de modèles group, de
type Model3DGroup, constitué par un cylindre (UIGeoCylindre) et deux tores
(UIGeoTore). L’ensemble peut être modifié par la propriété Dimension. Le
cylindre et les deux tores ont une propriété Material différente.

public class UICombinaison:UIElement3D {


protected override void OnUpdateModel() {
Model3DGroup group = new Model3DGroup();
UIGeoTore tore = new UIGeoTore();
UIGeoCylindre cylindre = new UIGeoCylindre();
GeometryModel3D modele_tore = new GeometryModel3D();
tore.Rayon1 = 0.6 * Dimension;
tore.Rayon2 = 0.1 * Dimension;
tore.Centre = Centre + new Vector3D(0, Dimension / 4, 0);
tore.DivisionH = 50;
tore.DivisionV = 50;
modele_tore.Geometry = tore.Maillage;
modele_tore.Material = ToreMaterial;
group.Children.Add(modele_tore);
modele_tore = new GeometryModel3D();
tore.Rayon1 = 0.65 * Dimension;
tore.Rayon2 = 0.2 * Dimension;
tore.Centre = Centre + new Vector3D(0, -Dimension / 4, 0);
modele_tore.Geometry = tore.Maillage;
modele_tore.Material = ToreMaterial;
group.Children.Add(modele_tore);
GeometryModel3D modele_cylindre = new GeometryModel3D();
cylindre.RayonInterne = 0.4 * Dimension;
cylindre.RayonExterne = 0.5 * Dimension;
cylindre.Hauteur = 1.5 * Dimension;
cylindre.Centre = Centre;
modele_cylindre.Geometry = cylindre.Maillage;
modele_cylindre.Material = CylindreMaterial;
group.Children.Add(modele_cylindre);
Copyright 2011 Patrice REY

Model = group;
}
...
}

La propriété Dimension permet de gérer globalement la dimension de


l’ensemble. C’est une propriété qui est propre à l’ensemble mais qui n’a rien à
voir avec les propriétés du cylindre et des tores. Cette propriété de dépendance,
CHAPITRE 7 □ L’interactivité des modèles 3D 223
de type double, est déclarée de la même façon que précédemment.

public static readonly DependencyProperty DimensionPropriete =


DependencyProperty.Register(«Dimension»,
typeof(double),
typeof(UICombinaison),
new PropertyMetadata(2.0, ProprieteModifiee));
public double Dimension {
get { return (double)GetValue(DimensionPropriete); }
set { SetValue(DimensionPropriete, value); }
}
private static void ProprieteModifiee(DependencyObject d,
DependencyPropertyChangedEventArgs e) {
UICombinaison combine = (UICombinaison)d;
combine.InvalidateModel();
}

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.

public static readonly DependencyProperty CylindreMaterialPropriete =


DependencyProperty.Register(«CylindreMaterial»,
typeof(Material),
typeof(UICombinaison),
new PropertyMetadata(new DiffuseMaterial(Brushes.Blue),
ProprieteModifiee));
public Material CylindreMaterial {
get { return (Material)GetValue(CylindreMaterialPropriete); }
set { SetValue(CylindreMaterialPropriete, value); }
}
public static readonly DependencyProperty ToreMaterialPropriete =
DependencyProperty.Register(«ToreMaterial»,
typeof(Material),
typeof(UICombinaison),
new PropertyMetadata(new DiffuseMaterial(Brushes.Blue),
ProprieteModifiee));
public Material ToreMaterial {
get { return (Material)GetValue(ToreMaterialPropriete); }
set { SetValue(ToreMaterialPropriete, value); }
}

Après avoir ajouté une référence à l’espace de noms xmlns:obj = «clr-


224 La programmation graphique 3D de WPF 4
namespace:Prog3dWpf4.chapitre07», la combinaison de modèles est positionnée
avec une dimension de 1.5. Les textures appliquées sur le cylindre et les deux
tores sont explicitées.

<!-- combinaison -->


<obj:UICombinaison x:Name='x_combi' Dimension='1.5'
MouseLeftButtonDown='x_combi_MouseLeftButtonDown'>
<obj:UICombinaison.CylindreMaterial>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_cylindre/texture-cylindre-04.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICombinaison.CylindreMaterial>
<obj:UICombinaison.ToreMaterial>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush
ImageSource='/Prog3dWpf4;component/contenu/
personalise_tore/texture-tore-03.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICombinaison.ToreMaterial>
</obj:UICombinaison>

Une glissière de type Slider est assignée à la modification de la valeur de la


propriété Dimension de la combinaison de modèles par le procédé du Binding.
On fait varier cette valeur entre 0 et 3, elle est fixée au départ à 1.5 par défaut.

<Slider Grid.Column='1' Name='slider1'


Value='{Binding ElementName=x_combi, Path=Dimension}'
Minimum='0' Maximum='3' />
Copyright 2011 Patrice REY

Un gestionnaire d’événements est attaché au clic gauche enfoncé sur la


combinaison de modèles. Lorsqu’il se déclenche, une fenêtre de type
MessageBox s’affiche en indiquant au format texte, la dimension de la
combinaison.

//clic sur le cylindre


CHAPITRE 7 □ L’interactivité des modèles 3D 225
private void x_combi_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e) {
UICombinaison le_cylindre = (UICombinaison)sender;
string message = «clic gauche enfoncé sur le combiné» + RC;
message += «dimension = « + le_cylindre.Dimension.ToString()
+ « unités» + RC;
MessageBox.Show(message);
}

3 - Les manipulations 3D

La modélisation des modèles 3D est généralement perçue comme une


opération intuitive pour être performante. Il est donc nécessaire lors de la
conception de modèles 3D, de pouvoir les sélectionner dans l’espace 3D par
un simple clic de souris (c’est le test d’atteinte) et de pouvoir les visualiser
sous différents angles par des mouvements de la souris (c’est l’utilisation d’un
TrackBall).
Nous allons voir en premier comment implémenter le test d’atteinte pour
sélection un objet 3D dans le Viewport3D, puis en second comment implémenter
un TrackBall pour faire tourner la scène 3D dans tous les sens. Nous verrons
en dernier la façon de réaliser un objet 3D avec des faces texturées différentes.
L’UserControl ExTestAtteinte.xaml, dans le dossier chapitre07, permet de visualiser
un modèle 3D composé de deux cylindres: le premier cylindre au centre du
repère est recouvert par un placage unique sur toute sa surface, et le deuxième
cylindre est recouvert par quatre placages différents (une texture pour la
surface extérieure, une texture pour la surface intérieure, une texture pour la
surface du dessus et une texture pour la surface du dessous). Un test d’atteinte
est implémenté de façon à indiquer si un clic gauche de la souris est intercepté
sur la surface d’un des deux cylindres. Un TrackBall est implémenté de façon
à visualiser la scène sous différents angles, en fonction des mouvements de la
souris. La figure 7-3 montre des copies d’écran des effets implémentés.

3.1 - Implémenter le test d’atteinte

Le test d’atteinte est un niveau basique d’interaction avec la souris. Il signifie


qu’un clic dans la zone de rendu 3D sur un objet 3D permet de sélectionner
l’objet 3D en question, ou bien que le test d’atteinte retourne une formulation
indiquant quel est l’objet 3D trouvé.
226 La programmation graphique 3D de WPF 4
Figure 7-3

Copyright 2011 Patrice REY


CHAPITRE 7 □ L’interactivité des modèles 3D 227
Un contrôle ToolBar, intitulé x_toolbar, est composé de deux boutons de type
RadioButton: le premier bouton d_rot_scene affiche un texte «Rs» et sa sélection
permet d’effectuer une rotation de la scène avec un TrackBall; le deuxième
bouton d_atteinte affiche un texte «At» et sa sélection permet d’effectuer un
test d’atteinte sur le Viewport3D.

<ToolBar x:Name='x_toolbar' Canvas.Left='633' Canvas.Top='254'


Width='299'>
<RadioButton x:Name='d_rot_scene' IsChecked='False' ToolTip='carré'>
<Grid>
<TextBlock Canvas.Left='651' Canvas.Top='261' Height='23'
Text='Rs' FontSize='18' Grid.Column='0'
Foreground='Crimson' TextAlignment='Center'
Margin='5'></TextBlock>
</Grid>
</RadioButton>
<RadioButton x:Name='d_atteinte' IsChecked='False' ToolTip='carré'>
<Grid>
<TextBlock Canvas.Left='651' Canvas.Top='261' Height='23'
Text='At' FontSize='18' Grid.Column='0'
Foreground='Crimson' TextAlignment='Center'
Margin='5'></TextBlock>
</Grid>
</RadioButton>
</ToolBar>

Un gestionnaire d’événement x_scene_3d_MouseLeftButtonDown est ajouté


en réponse au clic gauche enfoncé de la souris lorsque cette dernière est
positionnée sur la zone de rendu 3D, au niveau du ContainerUIElement3D x_
scene_3d. Si le RadioButton d_atteinte est enfoncé (d_atteinte.IsChecked == true),
le gestionnaire d’événement s’exécute.
Pour effectuer le test d’atteinte, il faut relever la position de la souris pos_
souris sur le x_viewport par la méthode GetPosition(..). Ensuite il faut passer en
paramètre cette position et le x_viewport à la méthode statique VisualTreeHelper.
HitTest(..).
La classe VisualTreeHelper fournit des méthodes utilitaires qui exécutent des
tâches courantes impliquant des nœuds dans une arborescence visuelle.
Le but de la méthode HitTest(..) est de renvoyer l’objet qui a été trouvé au
premier niveau d’un test de positionnement dans l’arborescence de l’objet
de type Visual passé en paramètre. La variable result, de type HitTestResult,
228 La programmation graphique 3D de WPF 4
retourne l’objet trouvé dans le test de positionnement de premier niveau
dans l’arborescence visuelle de x_viewport. Pour déterminer les coordonnées
3D du point relatif au test d’atteinte, il faut utiliser un objet mesh, de type
RayMeshGeometry3DHitTestResult, qui représente une intersection entre
un test d’atteinte à rayon et un MeshGeometry3D. Si mesh est instancié
(différent de null), il existe donc une intersection entre un test d’atteinte à
rayon et un maillage. L’intersection est visualisée par la propriété PointHit de
RayMeshGeometry3DHitTestResult. On détermine aisément les coordonnées 3D
du point d’intersection par PointHit.X, PointHit.Y et PointHit.Z. La propriété
VisualHit de HitTestResult retourne l’objet visuel qui a été testé. Si cet objet
est x_cylindre_1 (result.VisualHit == x_cylindre_1), alors on génère une chaîne
de texte à afficher dans la zone de texte x_infos et on indique les coordonnées
3D du point d’atteinte et le nom du cylindre concerné. Une animation, de
type Point3DAnimation, est déclenchée. Elle dure 0.2 secondes et consiste à
déplacer le cylindre d’une certaine distance avec un mouvement d’aller-
retour. La méthode BeginAnimation(..) applique l’animation pa à la propriété
CentrePropriete du cylindre de type UICylindreV2.

//clic sur le cylindre


private void x_scene_3d_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e) {
if (d_atteinte.IsChecked == true) {
Point pos_souris = e.GetPosition(x_viewport);
HitTestResult result =
VisualTreeHelper.HitTest(x_viewport, pos_souris);
RayMeshGeometry3DHitTestResult mesh =
result as RayMeshGeometry3DHitTestResult;
Point3DAnimation pa = new Point3DAnimation();
if (mesh != null) {
//
double x = Math.Round(mesh.PointHit.X, 3);
double y = Math.Round(mesh.PointHit.Y, 3);
double z = Math.Round(mesh.PointHit.Z, 3);
Copyright 2011 Patrice REY

Point3D pt = new Point3D(x, y, z);


string message = «clic gauche enfoncé sur :» + RC;
message += «(« + pt.ToString() + «)» + RC;
if (result.VisualHit == x_cylindre_1) {
message += «cylindre 4 faces texturées» + RC;
pa.From = x_cylindre_1.Centre;
pa.To = pt;
pa.Duration = TimeSpan.FromSeconds(0.2);
CHAPITRE 7 □ L’interactivité des modèles 3D 229
pa.AutoReverse = true;
x_cylindre_1.BeginAnimation(UICylindreV2.CentrePropriete,
pa);
}
...
x_infos.Text = message;
}
}
}

Si l’objet concerné par le test d’atteinte est x_cylindre_2 (result.VisualHit == x_


cylindre_2), alors on génère une chaîne de texte à afficher dans la zone de texte
x_infos et on indique les coordonnées 3D du point d’atteinte et le nom du
cylindre concerné. Une animation, de type Point3DAnimation, est déclenchée.
Elle dure 0.2 secondes et consiste à déplacer le cylindre d’une certaine
distance (jusqu’au point (2,0,0) de type Point3D) avec un mouvement d’aller-
retour. La méthode BeginAnimation(..) applique l’animation pa à la propriété
CentrePropriete du cylindre de type UICylindre.
En l’occurence, nous avons ici deux types de cylindre: le premier de type
UICylindre est un cylindre avec une texture unique, et le deuxième de type
UICylindreV2 est un cylindre avec 4 textures différentes (une texture par face).

//clic sur le cylindre


private void x_scene_3d_MouseLeftButtonDown(object sender,
MouseButtonEventArgs e) {
if (d_atteinte.IsChecked == true) {
Point pos_souris = e.GetPosition(x_viewport);
HitTestResult result =
VisualTreeHelper.HitTest(x_viewport, pos_souris);
RayMeshGeometry3DHitTestResult mesh =
result as RayMeshGeometry3DHitTestResult;
Point3DAnimation pa = new Point3DAnimation();
if (mesh != null) {
//
double x = Math.Round(mesh.PointHit.X, 3);
double y = Math.Round(mesh.PointHit.Y, 3);
double z = Math.Round(mesh.PointHit.Z, 3);
Point3D pt = new Point3D(x, y, z);
string message = «clic gauche enfoncé sur :» + RC;
message += «(« + pt.ToString() + «)» + RC;
...
230 La programmation graphique 3D de WPF 4
if (result.VisualHit == x_cylindre_2) {
message += «cylindre normal» + RC;
pa.From = x_cylindre_2.Centre;
pa.To = new Point3D(2, 0, 0);
pa.Duration = TimeSpan.FromSeconds(0.2);
pa.AutoReverse = true;
x_cylindre_2.BeginAnimation(UICylindre.CentrePropriete, pa);
}
x_infos.Text = message;
}
}
}

3.2 - Implémenter un TrackBall

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

les gestionnaires d’événements MouseLeftButtonDown, MouseMove et


MouseLeftButtonUp sur le Viewport3D. La démarche consiste à cliquer gauche
sur le Viewport3D et à maintenir le clic en déplaçant la souris, pour provoquer
les rotations adéquates de la scène 3D. Le relâchement du clic met fin aux
rotations.
Si le RadioButton d_rot_scene est enfoncé (d_rot_scene.IsChecked == true), on
détermine la position v_pos_souris de départ de la souris sur le Viewport3D par
CHAPITRE 7 □ L’interactivité des modèles 3D 231
Figure 7-4 Viewport3D

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().

private void x_viewport_MouseLeftButtonDown(object sender,


MouseButtonEventArgs e) {
if (d_rot_scene.IsChecked == true) {
e.Handled = true;
UIElement el = (UIElement)sender;
v_pos_souris_depart = e.MouseDevice.GetPosition(el);
el.CaptureMouse();
}
}

Dès que le déplacement de la souris est capturé sur le Viewport3D, on évalue le


vecteur delta qui représente le déplacement de la souris de la position initiale
(v_pos_souris) vers la nouvelle position (e.MouseDevice.GetPosition(el) de la
souris). Le vecteur mouse établit le vecteur avec seulement les composantes X
et Y de delta. Le vecteur axis calcule le produit croisé des structures mouse et
de celle de l’axe Z. La variable v_rotation_delta représente une rotation en trois
dimensions, de type Quaternion, avec axis et len (len est la longueur du vecteur
axis). Il ne reste plus qu’à établir la transformation RotateTransform3D à partir
d’une instance QuaternionRotation3D, et à l’affecter à la propriété Transform de
x_scene_3d.

private void x_viewport_MouseMove(object sender, MouseEventArgs e) {


if (d_rot_scene.IsChecked == true) {
e.Handled = true;
UIElement el = (UIElement)sender;
if (el.IsMouseCaptured) {
Vector delta = v_pos_souris_depart
- e.MouseDevice.GetPosition(el);
delta /= 2;
Vector3D mouse = new Vector3D(delta.X, -delta.Y, 0);
Copyright 2011 Patrice REY

Vector3D axis = Vector3D.CrossProduct(mouse,


new Vector3D(0, 0, 1));
double len = axis.Length;
if (len < 0.00001) {
v_rotation_delta = new Quaternion(new Vector3D(0, 0, 1), 0);
}
else {
v_rotation_delta = new Quaternion(axis, len);
CHAPITRE 7 □ L’interactivité des modèles 3D 233
}
Quaternion q = v_rotation_delta * v_rotation;
RotateTransform3D rotation = new RotateTransform3D();
QuaternionRotation3D quatRotation = new
QuaternionRotation3D(q);
rotation.Rotation = quatRotation;
rotation.CenterX = 0;
rotation.CenterY = 0;
x_scene_3d.Transform = rotation;
}
}
}

Au relâchement du clic gauche de la souris, la capture de cette dernière sur le


Viewport3D est arrêtée. Les variables sont remises à jour.

private void x_viewport_MouseLeftButtonUp(object sender,


MouseButtonEventArgs e) {
if (d_rot_scene.IsChecked == true) {
e.Handled = true;
v_rotation = v_rotation_delta * v_rotation;
UIElement el = (UIElement)sender;
el.ReleaseMouseCapture();
}
}

Le bas de la figure 7-3 montre des copies d’écran de l’effet obtenu lors des
transformations de rotation.

3.3 - Objet 3D avec plusieurs textures

Dans l’UserControl ExTestAtteinte.xaml, un des deux cylindres (celui en forme


d’anneau) possède une texture différente sur les quatre faces (extérieure,
intérieure, dessus et dessous). Pour réaliser cela, il faut différencier les
maillages.
On duplique la classe UIGeoCylindre et on la renomme UIGeoCylindreV2. La
différenciation des maillages consiste à déclarer quatre nouvelles propriétés
MaillageDessus, MaillageDessous, MaillageExterne et MaillageInterne, qui
obtiennent les maillages concernés par l’intermédiaire des méthodes de la
forme ObtenirMaillageFace-xxx-3D(..).
234 La programmation graphique 3D de WPF 4
//propriete publique maillage (lecture)
public MeshGeometry3D Maillage {
get { return ObtenirMaillage3D(); }
}
public MeshGeometry3D MaillageDessus {
get { return ObtenirMaillageFaceDessus3D(); }
}
public MeshGeometry3D MaillageExterne {
get { return ObtenirMaillageFaceExterne3D(); }
}
public MeshGeometry3D MaillageDessous {
get { return ObtenirMaillageFaceDessous3D(); }
}
public MeshGeometry3D MaillageInterne {
get { return ObtenirMaillageFaceInterne3D(); }
}

Ensuite, avec par exemple la méthode ObtenirMaillageFaceDessus3D(), on


sélectionne que les points concernés pour l’édition de la face du dessus. Ces
points sont envoyés à la méthode DessinerFacette(..).

//definition du maillage face dessus


private MeshGeometry3D ObtenirMaillageFaceDessus3D() {
MeshGeometry3D mesh = new MeshGeometry3D();
if (DivisionTheta < 2 || RayonInterne == RayonExterne) {
return null;
}
double radius = RayonInterne;
if (RayonInterne > RayonExterne) {
RayonInterne = m_rayon_ext;
RayonExterne = radius;
}
double h = Hauteur / 2;
Point3D[,] pts = new Point3D[DivisionTheta, 4];
for (int i = 0; i < DivisionTheta; i++) {
Copyright 2011 Patrice REY

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;
}

La méthode DessinerFacette(..) reçoit quatre points pour lesquels elle réalise la


facette en ajoutant à la propriété Positions de mesh, les points concernés, puis
en ajoutant à la propriété TriangleIndices les indices dans l’ordre nécessaire,
et en ajoutant à la propriété TextureCoordinates les points de correspondance
pour l’application de la texture.

private void DessinerFacette(Point3D pt0, Point3D pt1, Point3D pt2,


Point3D pt3, MeshGeometry3D mesh, int numero) {
mesh.Positions.Add(pt0);
mesh.Positions.Add(pt1);
mesh.Positions.Add(pt2);
mesh.Positions.Add(pt3);
mesh.TriangleIndices.Add(numero);
mesh.TriangleIndices.Add(numero + 1);
mesh.TriangleIndices.Add(numero + 2);
mesh.TriangleIndices.Add(numero + 2);
mesh.TriangleIndices.Add(numero + 3);
mesh.TriangleIndices.Add(numero);
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));
}

Les quatre maillages sont ainsi générés séparément. Maintenant, on duplique


la classe UICylindre et on la renomme UICylindreV2. Un groupe de maillages, de
type Model3DGroup, intitulé groupe, est instancié et reçoit dans sa propriété
Children quatre GeometryModel3D intitulés face_dessus, face_dessous, face_externe
et face_interne. Chaque objet GeometryModel3D a sa propriété Geometry affectée
236 La programmation graphique 3D de WPF 4
par un maillage correspondant (UIGeoCylindreV2.MaillageDessus, etc.).

public class UICylindreV2 : UIElement3D {


protected override void OnUpdateModel() {
Model3DGroup groupe = new Model3DGroup();
Modele = groupe;
GeometryModel3D face_dessus = new GeometryModel3D();
UIGeoCylindreV2 geo_dessus = new UIGeoCylindreV2();
...
face_dessus.Geometry = geo_dessus.MaillageDessus;
face_dessus.Material = MaterialDessus;
groupe.Children.Add(face_dessus);
GeometryModel3D face_externe = new GeometryModel3D();
UIGeoCylindreV2 geo_externe = new UIGeoCylindreV2();
...
face_externe.Geometry = geo_externe.MaillageExterne;
face_externe.Material = MaterialExterne;
groupe.Children.Add(face_externe);
GeometryModel3D face_dessous = new GeometryModel3D();
UIGeoCylindreV2 geo_dessous= new UIGeoCylindreV2();
...
face_dessous.Geometry = geo_dessous.MaillageDessous;
face_dessous.Material = MaterialDessous;
groupe.Children.Add(face_dessous);
GeometryModel3D face_interne = new GeometryModel3D();
UIGeoCylindreV2 geo_interne = new UIGeoCylindreV2();
...
face_interne.Geometry = geo_interne.MaillageInterne;
face_interne.Material = MaterialInterne;
groupe.Children.Add(face_interne);
}
...
}

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.

//la propriete de dependance de UICylindre: MaterialDessus


public static readonly DependencyProperty MaterialDessusPropriete =
DependencyProperty.Register(«MaterialDessus»,
typeof(Material),
typeof(UICylindreV2),
CHAPITRE 7 □ L’interactivité des modèles 3D 237
new PropertyMetadata(new DiffuseMaterial(
Brushes.LightCyan), ProprieteModifiee));
public Material MaterialDessus {
get { return (Material)GetValue(MaterialDessusPropriete); }
set { SetValue(MaterialDessusPropriete, value); }
}
...

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.).

<!-- objet cylindre 1 -->


<obj:UICylindreV2 x:Name='x_cylindre_1'
RayonExterne='1.5' RayonInterne='1.2'
Hauteur='0.5'>
<obj:UICylindreV2.MaterialDessus>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrushImageSource='/Prog3dWpf4;component/contenu/
personalise_cylindre/texture-cylindre-04.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICylindreV2.MaterialDessus>
<obj:UICylindreV2.MaterialExterne>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrushImageSource='/Prog3dWpf4;component/contenu/
personalise_cylindre/texture-cylindre-03.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICylindreV2.MaterialExterne>
<obj:UICylindreV2.MaterialDessous>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrushImageSource='/Prog3dWpf4;component/contenu/
personalise_cube/texture-cube-02.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICylindreV2.MaterialDessous>
<obj:UICylindreV2.MaterialInterne>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
238 La programmation graphique 3D de WPF 4
<ImageBrush ImageSource='/Prog3dWpf4;component/contenu/image/
texture-dodecaedre.png'></ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
</obj:UICylindreV2.MaterialInterne>
</obj:UICylindreV2>

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

L’UserControl ExViewport2DVisual3D.xaml, dans le dossier chapitre07, permet


de visualiser des éléments 2D qui sont intégrés dans des objets 3D. En
l’occurrence, un cube est composé de six faces dont trois faces (devant, gauche
et arrière) reçoivent des éléments 2D. Le cube peut être visualisé sous tous les
angles grâce à l’implémentation de l’effet TrackBall (effet réalisé au paragraphe
précédent). Le clic, sur un des boutons se trouvant sur les faces, provoque
l’affichage du nom du bouton cliqué dans la zone de texte «information». La
figure 7-5 montre le résultat obtenu.
WPF autorise l’intégration des éléments 2D interactifs dans une scène 3D.
Cette fonctionnalité est utile pour afficher du texte ou réaliser des projections
d’éléments 2D en perspective. Bien évidemment l’interactivité des éléments
2D est préservée. La classe 3D qui supporte les contrôles 2D est un Visual3D
qui fait office de Viewport2D et s’appelle donc Viewport2DVisual3D.
Comme le visualise la figure 7-6, un objet Viewport2DVisual3D dispose
d’une propriété Geometry et d’une propriété Material tout comme un objet
GeometryModel3D. Parmi les matières spécifiées dans Material, la matière qui
supporte l’élément 2D doit être spécifiée au moyen de la propriété attachée
booléenne Viewport2DVisual3D.IsVisualHostMaterial (valeur égale à true). Le
conteneur des contrôles 2D doit être spécifié dans sa propriété Visual (propriété
Copyright 2011 Patrice REY

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.

<!-- surface avant **************************** -->


<Viewport2DVisual3D>
CHAPITRE 7 □ L’interactivité des modèles 3D 239
Figure 7-5
240 La programmation graphique 3D de WPF 4
Figure 7-6

<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>

Ensuite on ajoute un DiffuseMaterial à la propriété Material de Viewport2DVisual3d,


en fixant la propriété IsVisualHostMaterial à true.

<!-- surface avant **************************** -->


<Viewport2DVisual3D>
...
<Viewport2DVisual3D.Material>
<DiffuseMaterial
Copyright 2011 Patrice REY

Viewport2DVisual3D.IsVisualHostMaterial='true'
Brush='White' />
</Viewport2DVisual3D.Material>
...
</Viewport2DVisual3D>

Enfin on ajoute un conteneur dans la propriété implicite Visual, qui est


constitué des contrôles 2D à appliquer.
CHAPITRE 7 □ L’interactivité des modèles 3D 241
<!-- surface avant **************************** -->
<Viewport2DVisual3D>
...
<!-- elements 2d -->
<Border BorderBrush='Magenta' BorderThickness='2'>
<Border.Background>
<RadialGradientBrush>
<GradientStop Color='DarkOrange' Offset='0' />
<GradientStop Color='SkyBlue' Offset='1' />
</RadialGradientBrush>
</Border.Background>
<StackPanel Margin='10'>
<TextBlock Margin='5' Foreground='Black'>Face avant</TextBlock>
<Button Margin='5' Click='Button_Click'
Foreground='Crimson' Cursor='Hand'
Name='x_btn_avant'>Cliquez moi</Button>
<TextBox Margin='5' Foreground='Blue'>Texte devant</TextBox>
</StackPanel>
</Border>
</Viewport2DVisual3D>

Comme le montre la figure 7-5, en orientant le cube, si on clique sur un des


boutons, la zone x_infos affiche le nom du bouton qui a été cliqué (gestionnaire
Button_Click attaché à chacun des boutons).
CHAPITRE 8

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.

1 - Générer une surface 3D

L’UserControl ExSurface.xaml, dans le dossier chapitre08, visualise la création


de 3 surfaces 3D à partir de 3 équations différentes. Ces équations génèrent
des surfaces assimilées à des terrains (très souvent utilisées dans les jeux). La
figure 8-1 montre les surfaces obtenues.
Mathématiquement parlant, pour une paire de coordonnées x et z, on calcule
une coordonnée y selon une équation. Cette coordonnée y est en faite une
élévation ou un abaissement de la surface. La démarche consiste à calculer
les points (x,y,z) et à afficher les facettes rectangles (contenant deux triangles)
avec l’application d’une texture de chaque côté.
La première chose à effectuer est de créer une classe Surface qui va servir à
générer et à afficher le résultat des équations des surfaces. Un objet Surface
contient divers champs privés avec notamment des champs qui encadrent
l’étendue des valeurs suivant X et Z (m_x_min, m_z_min, etc.), le nombre de
divisions souhaité suivant X et Z (plus ces grandeurs sont importantes, m_
div_x et m_div_z, plus la définition sera meilleure), un centre m_centre pour la
localisation, un modèle m_modele de type GeometryModel3D pour l’affichage
de l’objet 3D, et les textures à appliquer (m_texture_dessus et m_texture_dessous).
Tous ces champs sont accessibles par leurs accesseurs respectifs.

public class Surface {


//champs prives
244 La programmation graphique 3D de WPF 4
Figure 8-1

Copyright 2011 Patrice REY


CHAPITRE 8 □ Les surfaces 3D 245
public delegate Point3D FonctionCalcul(double x, double z);
private double m_x_min;
private double m_x_max;
private double m_y_min;
private double m_y_max;
private double m_z_min;
private double m_z_max;
private int m_div_x;
private int m_div_z;
private Point3D m_centre;
private GeometryModel3D m_modele;
private MeshGeometry3D m_mesh;
private DiffuseMaterial m_texture_dessus;
private DiffuseMaterial m_texture_dessous;
private string m_chem_abs_texture_dessus;
private string m_chem_abs_texture_dessous;
...
}

A noter l’ajout d’un délégué FonctionCalcul(double x, double z) pour permettre


de calculer une variable y et de l’affecter à un moment donné, sachant que
cette variable résulte d’un calcul lié à une procédure qui retourne un résultat.
La méthode DefinirLesTextures() permet d’assigner les textures à appliquer sur
le dessus et sur le dessous de la surface (en signifiant les chemins qui pointent
sur les ressources de type image).

public void DefinirLesTextures() {


ImageBrush img_brush_dessus = new ImageBrush();
img_brush_dessus.ImageSource = new BitmapImage(
new Uri(m_chem_abs_texture_dessus, UriKind.Absolute));
m_texture_dessus.Brush = img_brush_dessus;
ImageBrush img_brush_dessous = new ImageBrush();
img_brush_dessous.ImageSource = new BitmapImage(
new Uri(m_chem_abs_texture_dessous, UriKind.Absolute));
m_texture_dessous.Brush = img_brush_dessous;
}

La création du maillage de la surface et son affichage se fait par la méthode


CreerMaillageSurface(..). C’est la méthode la plus importante de cette classe.
Elle consiste à parcourir, en fonction du nombre de divisions suivant X et
Z, la plage des valeurs x et z, et d’affecter le y correspondant en fonction de
246 La programmation graphique 3D de WPF 4
l’équation par l’intermédiaire du délégué f de type FonctionCalcul. On détermine
donc des points Point3D à afficher par rapport au centre, en prenant soin de les
normaliser par la méthode Normaliser(..). Ces points sont ensuite envoyés à
la méthode DessinerFacette(..) en vue de leur affichage (toujours avec le même
procédé d’affichage que celui employé dans les chapitres précédents).

public void CreerMaillageSurface(FonctionCalcul f) {


double dx = (Xmax - Xmin) / DivX;
double dz = (Zmax - Zmin) / DivZ;
if (DivX < 2 || DivZ < 2) {
return;
}
Point3D[,] pts = new Point3D[DivX, DivZ];
for (int i = 0; i < DivX; i++) {
double x = Xmin + i * dx;
for (int j = 0; j < DivZ; j++) {
double z = Zmin + j * dz;
pts[i, j] = f(x, z);
pts[i, j] += (Vector3D)Centre;
pts[i, j] = Normaliser(pts[i, j],
Xmin, Xmax, Ymin, Ymax, Zmin, Zmax);
}
}
int numero_indice = 0;
Point3D[] p = new Point3D[4];
for (int i = 0; i < DivX - 1; i++) {
for (int j = 0; j < DivZ - 1; j++) {
p[0] = pts[i, j];
p[1] = pts[i, j + 1];
p[2] = pts[i + 1, j + 1];
p[3] = pts[i + 1, j];
DessinerFacette(p[0], p[1], p[2], p[3], m_mesh, numero_indice);
numero_indice += 4;
}
}
Copyright 2011 Patrice REY

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

avec Valea une variable aléatoire


Copyright 2011 Patrice REY

Un GeometryModel3D possède une propriété Material et BackMaterial. Si la


texture appliquée sur une facette est par exemple de forme rectangle avec
le bord blanc et l’intérieur transparent (image au format PNG), la facette
CHAPITRE 8 □ Les surfaces 3D 249
apparaitra sous une forme filaire uniquement si cette même texture est
appliquée de chaque côté (Material et BackMaterial). C’est ce concept qui est
utilisé pour avoir la forme filaire des surfaces.

//texture transparente filaire


private void x_checkbox_2_Checked(object sender, RoutedEventArgs e) {
x_surfaces.Children.Clear();
switch (x_select.SelectedIndex) {
case 0:
GenererSurface01(v_chem_texture_fil, v_chem_texture_fil);
break;
case 1:
GenererSurface02(v_chem_texture_fil, v_chem_texture_fil);
break;
case 2:
GenererSurface03(v_chem_texture_fil, v_chem_texture_fil);
break;
}
}

Deux boutons permettent d’effectuer un zoom sur la scène. Pour réaliser


cette opération de rapprochement et d’éloignement, il faut appliquer une
transformation TranslateTransform3D à la propriété Transform de la caméra
par l’intermédiaire d’une valeur v_zoom qui stocke la puissance du zoom.
Les propriétés OffsetX, OffsetY et OffsetZ de la translation sont affectées de la
valeur v_zoom.

//zoom sur la scene


private void x_btn_zoom_plus_Click(object sender, RoutedEventArgs e)
{
Button btn = (Button)sender;
switch (btn.Name) {
case «x_btn_zoom_plus»:
v_zoom += -0.5;
x_camera.Transform =
new TranslateTransform3D(v_zoom, v_zoom, v_zoom);
break;
case «x_btn_zoom_moins»:
v_zoom += 0.5;
x_camera.Transform =
new TranslateTransform3D(v_zoom, v_zoom, v_zoom);
break; } }
250 La programmation graphique 3D de WPF 4

2 - Les surfaces paramétrées

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.

public class SurfaceParametrique {


//champs prives
public delegate Point3D FonctionCalcul(double u, double v);
private double m_x_min;
private double m_x_max;
private double m_y_min;
private double m_y_max;
private double m_z_min;
private double m_z_max;
private int m_div_u;
private int m_div_v;
private double m_u_min;
Copyright 2011 Patrice REY

private double m_u_max;


private double m_v_min;
private double m_v_max;
private Point3D m_centre;
private GeometryModel3D m_modele;
private MeshGeometry3D m_mesh;
private DiffuseMaterial m_texture_dessus;
private DiffuseMaterial m_texture_dessous;
CHAPITRE 8 □ Les surfaces 3D 251
Figure 8-5
252 La programmation graphique 3D de WPF 4
Figure 8-6

private string m_chem_abs_texture_dessus;


private string m_chem_abs_texture_dessous;
...
}

La génération du maillage par la méthode CreerMaillageSurface(..) se fait en


calculant le point (x,y,z) en fonction de u et v. La procédure de dessin de la
facette reste identique.

public void CreerMaillageSurface(FonctionCalcul f) {


Copyright 2011 Patrice REY

double du = (Umax - Umin) / (m_div_u - 1);


double dv = (Vmax - Vmin) / (m_div_v - 1);
if (m_div_u < 2 || m_div_v < 2) {
return;
}
Point3D[,] pts = new Point3D[m_div_u, m_div_v];
for (int i = 0; i < m_div_u; i++) {
double u = Umin + i * du;
CHAPITRE 8 □ Les surfaces 3D 253
for (int j = 0; j < m_div_v; j++) {
double v = Vmin + j * dv;
pts[i, j] = f(u, v);
pts[i, j] += (Vector3D)m_centre;
pts[i, j] = Normaliser(pts[i, j],
Xmin, Xmax, Ymin, Ymax, Zmin, Zmax);
}
}
int numero_indice = 0;
Point3D[] p = new Point3D[4];
for (int i = 0; i < m_div_u - 1; i++) {
for (int j = 0; j < m_div_v - 1; j++) {
p[0] = pts[i, j];
p[1] = pts[i, j + 1];
p[2] = pts[i + 1, j + 1];
p[3] = pts[i + 1, j];
DessinerFacette(p[0], p[1], p[2], p[3], m_mesh, numero_indice);
numero_indice += 4;
}
}}

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)

Xmin = -3, Xmax = +3,


Ymin = -3 * Math.PI, Ymax = +3 * Math.PI,
Zmin = -3, Zmax = +3,
DivU = 20, DivV = 20,
Umin = 0, Umax = 2*Math.PI,
Vmin = -0.5 * Math.PI, Vmax = 0.5 * Math.PI,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface02);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface02(double u, double v) {
double x = Math.Cos(v) * Math.Cos(u);
double z = Math.Cos(v) * Math.Sin(u);
double y = Math.Sin(v);
return new Point3D(x, y, z);
}

La surface numéro 3 réalisée est un tore. Sa forme filaire et son équation


paramétrique sont visualisées sur la figure 8-9. Elle a une forme de tore
comme on a déjà pu le voir mais cette fois avec une équation paramétrique.
256 La programmation graphique 3D de WPF 4
Sa réalisation consiste à faire tourner un cercle autour d’une droite verticale
mais avec une certaine distance.
Figure 8-9

équation paramétrique :
x = cos(u) * (1 + a * cos(v))
y = a * sin(v)
z = sin(u) * (1 + a * cos(v))

Dans l’exemple, on fait varier u entre 0 et +2π, et v entre 0 et +2π. Le code de


programmation du tore avec une équation paramétrique est le suivant:

//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);
}

La surface numéro 4 réalisée est une hyperboloïde. Sa forme filaire et son


équation paramétrique sont visualisées sur la figure 8-10. Sa réalisation
consiste à faire tourner une portion d’hyperbole autour d’une droite verticale
mais avec une certaine distance.
Figure 8-10

équation paramétrique :
x = a * cos(u) * cosh(v)
y = b * sinh(v)
z = b * sin(u) * cosh(v)

Dans l’exemple, on fait varier u entre 0 et +2π, et v entre -1 et +1. Le code


de programmation de l’hyperboloïde avec une équation paramétrique est le
suivant:

//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);
}

La surface numéro 5 réalisée est une paraboloïde. Sa forme filaire et son


équation paramétrique sont visualisées sur la figure 8-11. Sa réalisation
consiste à faire tourner une portion de parabole autour d’une droite verticale.
Figure 8-11

équation paramétrique :
x = a * cosh(u) * v
y=v*v
z = b * sinh(u) * v
Copyright 2011 Patrice REY

Dans l’exemple, on fait varier u entre -2 et +2, et v entre -2 et +2. Le code


de programmation de la paraboloïde avec une équation paramétrique est le
suivant:
CHAPITRE 8 □ Les surfaces 3D 259
//surface 5 paraboloid
private void GenererSurface05(string chem_tex_dessus, string
chem_tex_dessous) {
SurfaceParametrique surface = new SurfaceParametrique() {
Centre = new Point3D(0, 0, 0),
Xmin = -5, Xmax = +5,
Ymin = 0, Ymax = +5,
Zmin = -5, Zmax = +5,
DivU = 20, DivV = 10,
Umin = -2, Umax = 2,
Vmin = -2, Vmax = 2,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface05);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface05(double u, double v) {
double x = 0.8 * v * Math.Cosh(u);
double z = 0.5 * v * Math.Sinh(u);
double y = v * v;
return new Point3D(x, y, z);
}

La surface numéro 6 réalisée est un cône elliptique. Sa forme filaire et son


équation paramétrique sont visualisées sur la figure 8-12.
Dans l’exemple, on fait varier u entre 0 et +2π, et v entre -1 et +1. Le code
de programmation du cône elliptique avec une équation paramétrique est le
suivant:

//surface 6 le cone elliptique


private void GenererSurface06(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 = 20, DivV = 10,
Umin = 0, Umax = 2 * Math.PI,
260 La programmation graphique 3D de WPF 4
Figure 8-12

équation paramétrique :
x = a * cos(u) * v
y=b*v
z = c * sin(u) * v

Vmin = -1, Vmax = 1,


CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface06);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface06(double u, double v) {
double x = 1.2 * v * Math.Cos(u);
double z = 0.8 * v * Math.Sin(u);
double y = 0.9 * v;
return new Point3D(x, y, z);
}

La surface numéro 7 réalisée est un cylindre elliptique. Sa forme filaire et son


Copyright 2011 Patrice REY

équation paramétrique sont visualisées sur la figure 8-13.


Dans l’exemple, on fait varier u entre 0 et +2π, et v entre -0.5 et +0.5. Le code
de programmation du cône elliptique avec une équation paramétrique est le
suivant:

//surface 7 le cylindre elliptique


CHAPITRE 8 □ Les surfaces 3D 261
Figure 8-13

équation paramétrique :
x = a * cos(u)
y=v
z = b * sin(u)

private void GenererSurface07(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 = 40, DivV = 10,
Umin = 0, Umax = 2 * Math.PI,
Vmin = -0.5, Vmax = 0.5,
CheminAbsTextureDessus = chem_tex_dessus,
CheminAbsTextureDessous = chem_tex_dessous,
};
surface.DefinirLesTextures();
surface.CreerMaillageSurface(CalculYSurface07);
x_surfaces.Children.Add(surface.Modele);
}
//
private Point3D CalculYSurface07(double u, double v) {
double x = 1.2 * Math.Cos(u);
double z = 0.8 * Math.Sin(u);
double y = v;
return new Point3D(x, y, z);
}
262 La programmation graphique 3D de WPF 4

3 - Les surfaces extrudées

L’extrusion consiste à déplacer un profil (généralement courbé) le long d’un


chemin. Cela permet de générer une surface extrudée. Une caractéristique
importante fait que dans l’extrusion on obtient le même résultat en déplaçant
le profil le long du chemin ou bien en déplaçant le chemin le long du profil.
L’UserControl ExSurfaceExtrudee.xaml, dans le dossier chapitre08, visualise la
création de deux surfaces 3D extrudées. La figure 8-14 montre les surfaces
obtenues.
La classe SurfaceExtrudee permet de générer une surface extrudée à partir d’un
profil et d’un chemin. Cette classe se présente de la même manière que la
classe Surface du paragraphe 1. Les différences résident dans le fait qu’ici on
a deux listes de Point3D, la première est une liste qui stocke les points du
profil (m_liste_pts_profil) et la deuxième est une liste qui stocke les points du
chemin (m_liste_pts_chemin). Tous ces champs de données ont leurs accesseurs
respectifs.

public class SurfaceExtrudee {


//champs prives de travail
private List<Point3D> m_liste_pts_profil;
private List<Point3D> m_liste_pts_trace;
private double m_x_min;
private double m_x_max;
private double m_y_min;
private double m_y_max;
private double m_z_min;
private double m_z_max;
private GeometryModel3D m_modele;
private MeshGeometry3D m_mesh;
private DiffuseMaterial m_texture_dessus;
private DiffuseMaterial m_texture_dessous;
private string m_chem_abs_texture_dessus;
Copyright 2011 Patrice REY

private string m_chem_abs_texture_dessous;


private Point3D m_centre = new Point3D();
...
}

La méthode CreerMaillageSurface() permet de générer le maillage et de dessiner


les facettes. Elle consiste à créer un tableau de Point3D, à deux dimensions,
CHAPITRE 8 □ Les surfaces 3D 263
Figure 8-14
264 La programmation graphique 3D de WPF 4
contenant tous les points à éditer en fonction de la liste des points du profil
et ceux de la liste des points du chemin. Puis les points sont envoyés à la
méthode DessinerFacette(..) en vue de leur affichage.

public void CreerMaillageSurface() {


double dx, dy, dz;
//creer tous les points utilises
Point3D[,] pts =
new Point3D[ListePointsTrace.Count, ListePointsProfil.Count];
for (int i = 0; i < ListePointsTrace.Count; i++) {
// calculer le deplacement des pints
dx = ListePointsTrace[i].X - ListePointsTrace[0].X;
dy = ListePointsTrace[i].Y - ListePointsTrace[0].Y;
dz = ListePointsTrace[i].Z - ListePointsTrace[0].Z;
for (int j = 0; j < ListePointsProfil.Count; j++) {
pts[i, j].X = ListePointsProfil[j].X + dx;
pts[i, j].Y = ListePointsProfil[j].Y + dy;
pts[i, j].Z = ListePointsProfil[j].Z + dz;
pts[i, j] += (Vector3D)Centre;
pts[i, j] = Normaliser(pts[i, j], Xmin, Xmax, Ymin, Ymax, Zmin,
Zmax);
}
}
int numero_indice = 0;
Point3D[] p = new Point3D[4];
for (int i = 0; i < ListePointsTrace.Count - 1; i++) {
for (int j = 0; j < ListePointsProfil.Count - 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];
DessinerFacette(p[0], p[1], p[2], p[3], m_mesh, numero_indice);
numero_indice += 4;
}
}
}
Copyright 2011 Patrice REY

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

Après avoir instancié une nouvelle surface extrudée surf_extrudee, en fixant


les paramètres d’encadrement et de texture, une première boucle for permet
de remplir la liste des points utilisés pour le profil, et une deuxième boucle
for permet de remplir la liste des points utilisés pour le chemin. La méthode
DefinirLesTextures() fixe les textures choisies et la méthode CreerMaillageSurface()
génère la surface extrudée et texturée.

//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 figure 8-16 visualise la surface extrudée numéro 2. On procède de la même


manière pour réaliser l’extrusion.
Figure 8-16

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);
}

4 - Les surfaces de révolution

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.

public class SurfaceRevolution {


//champs prives de travail
private List<Point3D> m_liste_pts_profil;
private double m_x_min;
private double m_x_max;
private double m_y_min;
private double m_y_max;
private double m_z_min;
268 La programmation graphique 3D de WPF 4
Figure 8-17

Copyright 2011 Patrice REY


CHAPITRE 8 □ Les surfaces 3D 269
private double m_z_max;
private int m_div_theta;
private GeometryModel3D m_modele;
private MeshGeometry3D m_mesh;
private DiffuseMaterial m_texture_dessus;
private DiffuseMaterial m_texture_dessous;
private string m_chem_abs_texture_dessus;
private string m_chem_abs_texture_dessous;
private Point3D m_centre = new Point3D();
...
}

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; }
}
...

La création du maillage commence par le stockage de tous les points générés


par la réalisation de la surface. La liste m_liste_pts_profil stocke tous les points
par pas de m_div_theta sur la courbe servant de profil. Le tableau pts, à deux
dimensions, stocke les points Point3D en vue de leur affichage. La méthode
ObtenirPosition(..) permet d’obtenir leur position dans l’espace 3D en fonction
de certaines caractéristiques. La méthode DessinerFacette(..) permet l’envoi de
ces points en vue de la génération des facettes texturées.

public void CreerMaillageSurface() {


//creer tous les points utilises pour generer la surface
//(autour de l axe Y):
Point3D[,] pts = new Point3D[m_div_theta, m_liste_pts_profil.Count];
for (int i = 0; i < m_div_theta; i++) {
double theta = i * 2 * Math.PI / (m_div_theta - 1);
for (int j = 0; j < m_liste_pts_profil.Count; j++) {
double x = m_liste_pts_profil[j].X;
double z = m_liste_pts_profil[j].Z;
double r = Math.Sqrt(x * x + z * z);
pts[i, j] = ObtenirPosition(r, m_liste_pts_profil[j].Y, theta);
pts[i, j] += (Vector3D)m_centre;
pts[i, j] = Normaliser(pts[i, j], Xmin, Xmax, Ymin, Ymax, Zmin,
Zmax);
}
Copyright 2011 Patrice REY

}
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;
}
}
}

La figure 8-18 visualise le profil de la courbe qui va servir de support pour


la génération de la surface de révolution. Il s’agit d’une courbe plane, en
l’occurrence une simple sinusoïde sur quelques périodes. Cette courbe va
tourner autour de l’axe Y par pas de m_div_theta.
Figure 8-18

axe de
révolution Y

profil de la
surface de courbe
révolution
générée

La méthode GenererSurface01(..) procède à la génération de la surface de


révolution. Une surface surf_revol, de type SurfaceRevolution, est instanciée en
lui fixant les paramètres d’encadrement et les textures à appliquer. La liste des
points de la courbe plane est remplie en fonction de l’équation de la courbe.
Les textures sont fixées à leur ressource image respective. Puis le maillage est
généré par CreerMaillageSurface(). Enfin le modèle 3D est ajouté à la scène 3D.

//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);
}

Copyright 2011 Patrice REY


Annexe
Annexe 275
276 Annexe

Copyright 2011 Patrice REY


Annexe 277
278 Annexe

Copyright 2011 Patrice REY


Annexe 279
280 Annexe

Copyright 2011 Patrice REY


Annexe 281
282 Annexe

Copyright 2011 Patrice REY


Annexe 283
INDEX
Index 287

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)

chapitre 1 : les bases du graphisme 2D


chapitre 2 : les transformations 2D
chapitre 3 : les géométries et les tracés 2D
chapitre 4 : les couleurs et les pinceaux
chapitre 5 : les animations
chapitre 6 : les styles
chapitre 7 : les modèles de contrôles

En vente sur la boutique en ligne de www.decitre.fr


à l’adresse suivante:

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

En vente sur la boutique en ligne de www.decitre.fr


à l’adresse suivante:

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

En vente sur la boutique en ligne de www.decitre.fr


à l’adresse suivante:

http://www.decitre.fr/livres/Les-structures-de-donnees.aspx/9782810613878
La programmation graphique 3D de WPF 4

Windows Presentation Foundation (WPF) est une technologie de


développement d’application riche (Rich Desktop Application)
pour la plateforme Windows. Sa version actuelle est la version 4,
celle qui correspond au Framework .NET 4.0.
WPF est devenue une technologie majeure du développement
pour Windows en code managé. Ses innovations sont très
importantes, à la fois sur le plan architectural et sur le plan
fonctionnel. Elle constitue une synthèse et une unification des
différentes branches de l’informatique graphique (graphismes
Patrice REY est vectoriels 2D et 3D, saisie, animation, typographie et
informaticien et formateur multimédia).
indépendant, diplômé en
WPF est un framework managé qui exploite la puissance de
informatique et certifié
Microsoft MCTS (Client Direct3D pour l’affichage. Cette technologie de développement
Application Development). d’interface utilisateur permet de moderniser et d’unifier les
Passionné par les techniques de développement visuel, d’exploiter la puissance
technologies Microsoft
graphique des machines modernes, d’intégrer l’infographie dans
Silverlight et WPF, adepte
du langage C#, au travers le processus de développement, et de simplifier et sécuriser le
de cet ouvrage, il vous déploiement des applications.
fait profiter de son WPF sépare dans des couches distinctes la programmation des
expertise et vous fait
fonctionnalités (au moyen d’un langage .NET tel que le C#) de la
partager sa passion pour
le développement présentation visuelle par l’utilisation du langage XAML
d’applications. (eXtensible Application Markup Language).
Les différents chapitres permettent d’apprendre et de mettre en
pratique les notions essentielles de la programmation graphique
3D de WPF 4 avec l’utilisation de l’environnement intégré de
Visual Studio 2010 (dans sa version Express ou Ultimate).
Vous apprendrez notamment à programmer les vecteurs et les
matrices 3D, la projection perspective et la projection parallèle,
les transformations 3D (rotation, translation, mise à l’échelle), la
composition de la scène 3D (la zone de rendu, les modèles 3D,
les surfaces et textures, les lumières, les caméras), la
modélisation des objets (cube, sphère, cylindre, tore, cône,
icosaèdre, dodécaèdre), l’apport de l’interactivité des objets 3D
(test d’atteinte, TrackBall), et la création des modèles
Patrice
Pa
Pattri
trice
r ic
ce
eRREY
RE
EY surfaciques (avec les surfaces paramétrées, extrudées, et de
révolution). Tout le code source du livre est en téléchargement
Edition
Edit
i ion numé
n
numérique
ériqu
ériqu
uee gratuit sur le site http://www.reypatrice.fr
www.reypatrice.fr

ISBN : 978-2-8106-2086-9
Éditeur :
Books on Demand GmbH, 41 €
12/14 rond point des
Champs Élysées,
75008 Paris,
France

Vous aimerez peut-être aussi