La dernière fois j'ai décrit comment afficher des choses avec un VertexBuffer. Mais nous avions utilisé des coordonnées transformées ce qui veut dire que c'était à nous de calculer la position des sommets à l'écran. Si nous désirons écrire une vraie application Direct3D, ce serait très fastidieux de faire comme cela : imaginez la quantité de code nécessaire pour animer un tank tirant un obus en mettant à jour les milliers de sommets constituant la scène et en faisant tous les calculs mathématiques définissant la bonne position de chaque truc à l'écran.
Bien sûr, c'est le genre de choses qu'une librairie pourrait faciliter, et Direct3D fait exactement cela. Cependant, pour savoir comment tirer parti des capacités de Direct3D, nous devons comprendre le fonctionnement des systèmes de coordonnées et des transformations.
Un système de coordonnées n'est qu'un moyen de décrire les points dans l'espace. Il y en a de plusieurs type; vous avez probablement entendu parlé des coordonnées cartésiennes, des coordonnées sphériques, cylindriques ou autres. Ces systèmes de coordonnées peuvent tous être utile dans Direct3D, et parfois entrent en jeu dans des applications du monde réel. Si comme moi vous venez juste de démarrer avec Direct3D, il y en a un que vous rencontrerez plus que les autres : le système tri-dimentionnel cartésien orienté main gauche.
Ce système est assez simple. Les points sont positionnés dans l'espace par des coordonnées x, y et z. Les trois axes sont orthogonaux entre eux. La coordonnée x augmente horizontalement vers la droite. y croît vers le haut. Quand à z, il pointe vers l'interieur de l'écran, il s'éloigne du spectateur. Ceci est important car c'est l'inverse du système qu'on utilise à l'école ou dans les livres scientifiques. C'est un système orienté main droite et z augmente en se rapprochant du spectateur. En graphisme informatique, on utilise un système orienté main gauche afin que les coordonnées aient une signification plus naturelle : z devient alors un moyen de mesurer la distance qui nous sépare d'un objet.
Cette image montre la difference entre un système orienté main gauche et main droite :
Les mains sont utilisées comme moyen mémotechnique pour se rappeler de la direction des axes : pliez les doigts de votre main à 90°, vous obtenez les axes x et y, et le pouce indique la direction positive de l'axe z.
Lors du rendu d'une scène, il y a plusieurs étapes. Le moyen le plus simple de représenter un objet diffère entre chaque étape. Prenons une boîte (par exemple si je voulais modéliser un coffre à trésor dans un jeu), à la définition des sommets qui constituent la boîte, il serait probablement plus commode de considérer un coin de la boîte comme étant l'origine, et les autres coins auraient des coordonnées du genre (0, 0, 3), (0, 5, 0) et (3, 5, 0). Cela serait bien plus simple que si on prenait la vraie origine se trouvant à Perpète-les-bains ce qui impliquerait avoir des coordonnées de ce style (1.2345, 17.87, 12379809.78) et (478.12, 5873.321, 58.876).
Le système de coordonnées qui est facilement utilisable pour un objet donné est appelé le système de coordonnée local, ou espace local, ou espace objet.
Il y a un autre système de coordonnées qui est pratique pour la scène entière. Par exemple, imaginez que nous ayons une pièce avec un coffre, une table et une lampe dedans. Nous voudrions probablement un système de coordonnées différent de celui local au coffre. Nous souhaiterions peut être considérer un coin de la pièce comme étant l'origine dont les axes suivraient les murs et le sol. Cela rendrait plus simple le positionnement d'un objet donné : "le coffre est 3m à droite, posé sur le sol et 2m dedans" serait traduit en (3, 0, 2).
Nous appelons ce système coordonnées monde ou espace monde. L'idée est que c'est un système de coordonnées qui exprime la position des choses dans le monde. Chaque objet dans la scène ont leur propre ensemble de coordonnées locales mais ils partagent des coordonnées monde.
A ce stade nous avons besoin de trouver une solution pour passer de système en système. Après tout, disons qu'on voudrait mettre trois coffres à trésor dans la pièce; à moins qu'ils ne soient tous à la même place, nous allons avoir besoin de transformer les coordonnées locales (qui étaient bien pratiques pour définir la boîte) en un ensemble de trois systèmes différents de coordonnées monde afin que tout ait une position relative à tout le reste.
L'outil utilisé aussi bien en maths que dans le graphisme informatique pour aller d'un système à l'autre est la matrice. Vous devez savoir ce qu'elle est pour faire du Direct3D (ndt : non ce n'est pas le monde qu'on met devant tes yeux pour te cacher la vérité). Je ne vais même pas essayer de vous dire que vous pouvez vous en sortir sans rien savoir à ce sujet : vous ne pourrez pas, à moins de rester à des choses complètement basiques. Heureusement ce ne sont pas des maths super compliquées, allez dans Google "Introduction matrices" et vous tomberez sur un demi million de pages qui vous expliqueront les bases. Je vais partir du principe que vous en avez fait autant.
Si nous prenons les éléments de notre matrice avec attention, multiplier cette matrice par un ensemble de coordonnées dans un système modifie ces coordonnées en coordonnées équivalentes dans un autre système. C'est en cela qu'une matrice est un transformateur. Le transformateur qui nous aide à localiser notre objet dans l'espace monde (c a d qui modifie les coordonnées locales en coordonées monde) est letransformateur monde.
Les matrices dans Direct3D sont manipulables au travers de la structure Microsoft.DirectX.Matrix. Remarquez que c'est dans le namespace Microsoft.DirectX et non Microsoft.DirectX.Direct3D car les matrices sont aussi utilisées par d'autres membres de la famille DirectX.
Heureusement pour nous, la structure Matrix a un paquet de méthodes qui nous épargnent les calculs relativement barbants des coefficients des matrices. Enfin, d'habitude. Par exemple, nous pourrions avoir une matrice qui représente une rotation de 45° autour de l'axe Y comme cela :
Matrix worldTxfm = Matrix.RotationY(Math.PI / 4);
Remarquez que l'argument est un angle en radians, pas en degrés. C'est facile : 360° = 2Pi radians, 180° = Pi radians, 90° = Pi/2 radians etc.
Il y a encore deux autres transformateurs que vous rencontrerez habituellement en écrivant des applications Direct3D. Il s'agit du transformateur de vue et du transformateur de projection. Parlons tout d'abord du transformateur de vue.
Tandis que l'espace objet est un système de coordonnées pratique pour les objets dans la scène, certaines operations qui auraient besoin d'être faites sont plus facilement exprimables dans l'espace vue. Cela est parfois appelé espace camera car l'espace est defini comme ayant l'origine au point de camera (ou d'oeil). C'est à dire, quelquechose à (0, 0, 0) est co-localisé par le visualisateur (ndt : ??).
En plus de toujours mettre l'origine au point de camera, l'espace vue oriente l'axe z dans la même direction que la camera, et l'axe y vers le "haut". Nous pouvons avoir une matrice qui produit la transformation necessaire pour changer les coordonnées monde en coordonnées espace vue par le biais d'une méthode statique de Matrix : LookAtLH.
Matrix viewTxfm = Matrix.LookAtLH( new Vector3(0, 0, -5), // Camera positionné 5 unité "en dehors" de l'écran new Vector3(), // Regarde l'origine new Vector3(0, 1, 0) // La direction du "haut" est l'axe y );
LookAtLH prend trois arguments : position de la caméra, endroit où on regarde, direction du "haut". Toutes ces coordonnées sont fournies par le biais d'une structure Vector3 et chacune d'elles sont exprimées en coordonnées monde. Cela est vraiment très pratique étant donné que c'est une façon très naturelle de regarder la scène ; nous pouvons considérer la caméra comme étant un objet de la scène en exprimant sa position et son orientation dans le même système de coordonnées que les tanks, les balles, les explosions et les mitrailleuses qu'on est en train de dessiner. Oh et pis au cas où vous ne l'auriez pas deviné, LH signifie main gauche (left-hand).
L'espace vue est pratique pour les opérations graphiques qui ont un rapport avec la caméra (comme le z-buffering, un sujet que nous aborderons plus tard) mais ce n'est pas très pratique pour dessiner des choses à l'écran ce qui est notre but. Pour que les objets puissent passer d'un espace tri-dimentionnel à un espace écran bi-dimentionnel, nous utilisons le transformateur de projection. La structure Matrix fournit également une méthode :
Matrix projTxfm = Matrix.PerspectiveFovLH( (float)Math.PI/4.0F, // Champ de vision standard 1.0F, // Rapport d'aspect 1.0F, 10.0F // N'affiche que les choses qui sont distantes // de 1 à 10 unités de la caméra );
PerspectiveFovLH prend quatre arguments : un champ de vision, un rapport d'aspect et une paire de distances qui définissent les plans limite en profondeur.
Le champ de vision indique essentiellement quel genre de lentille on utilise : grand angle ou angle étroit. Des nombres plus grands ici permettrait de voir de plus en plus de choses à l'écran mais l'image serait de plus en plus déformée. Des nombres plus petits provoqueraient moins de déformations mais également moins de choses visibles. En général c'est mieux de rester avec une valeur de Pi/4 radians (45°).
Le rapport d'aspect indique le rapport entre la hauteur et la largeur de l'écran. Une valeur de 1.0 signifie que nous essayons d'afficher une image carré. Une valeur de 0.5 voudrais dire que nous voulons afficher une image deux fois moins haute que large, un peu comme un écran de cinéma ou HDTV (ndt : ?). Notez qu'en depis du rapport d'aspect, Direct3D étireras toujours l'image pour la faire tenir sur la fenêtre où on dessine. Ainsi il y aura des déformations à moins que la largeur ne soit égale à la hauteur.
Les deux derniers arguments spécifient les plans limite. Tout ce qui est plus proche de la camera que la première distance, ou plus loin que la deuxième distance, ne sera pas dessiné. Remarquez que ces distances concernent la caméra. Si un objet est partiellement en dedans et partiellement en dehors des bornes, il sera coupé. C'est à dire vous ne verrez que la partie de l'objet qui appartient à la fourchette de distances.
Il va de soi qu'il est assez important d'essayer de positionner les plans limite le plus proche possibles du début et de la fin de la scène. C'est tentant de leur affecter une très petite valeur et une très grande valeur, mais cela a tendance à faire foirer des choses comme le z-buffering. Personne n'a dit que faire de la bonne 3D était simple !
Ce que je ne vous ai pas montré est comment utiliser tout cela dans un programme qui dessine des pixels à l'écran. Mais avec ce petit bagage dans notre pantalon, ça ne prendra pas beaucoup de code. Je vous montrerai comment faire la prochaine fois.