La dernière fois nous avons défini comment initialiser Direct3D - comment créer un objet Device. Cependant nous n'avons pas encore tâté de code pour le rendu, la partie la plus marrante. Dans cet épisode, nous verrons les bases du procédé de rendu, nous apprendrons notamment l'ultime chose dans un scène 3D : effacer l'écran.
Souvenez vous, la boucle de jeu. Voici le code :
static void Main() { Game app = new Game(); app.InitializeGraphics(); app.Show(); while (app.Created) { app.Render(); Application.DoEvents(); } app.DisposeGraphics(); }
Au beau milieu du bidule, il y a un appel à Render. C'est cette methode que nous nous apprêtons à écrire et qui en fait s'occupe du dessin de notre scène 3D. En gros, il y a 5 choses à faire :
1. Effacer le back buffer. 2. Préparer Direct3D à dessiner. 3. Dessiner la scène. 4. Indiquer à Direct3D qu'on a fini de dessiner. 5. Copier le back buffer à l'écran.
Nous allons parler de tout sauf de l'étape 3. Le dessin des formes qui constuisent la scène nécessite d'avoir compris des trucs genre VertexBuffers ou système de coordonnées, donc nous nous réservons cela pour une future discution. Cependant, les 4 autres étapes sont vitales pour faire marcher le rendu. Voici le code qui les implémente :
protected void Render() { // Efface le back buffer device.Clear(ClearFlags.Target, Color.Black, 1.0F, 0); // Prépare Direct3D à dessiner device.BeginScene(); // Dessiner la scène - les appels aux méthodes de rendu viennent ici // Indique à Direct3D qu'on a fini de dessiner device.EndScene(); // Copie le back buffer à l'écran device.Present(); }
La tendance de ces dernières années est de faire de plus en plus abstraction de choses bas-niveau lorsque nous programmons : le CLR nous permet d'ignorer grandement le système d'exploitation sur lequel tourne l'application. Mais lorsque nous travaillons avec Direct3D, il est bon de ne pas oublier qu'il y a une forte relation avec le matos; même si la puissance des processeurs et les performances des cartes graphiques actuelles sont abracadabrantes, il faut être attentif à ce que nous faisons pour avoir des resultats convenables.
C'est pour cela que Direct3D nous fournit la capacité d'utiliser des back buffers. Retenez juste que c'est une zone mémoire dans laquelle nous dessinons au lieu de dessiner directement à l'écran. Cela nous épargne de faire aller et venir des tonnes d'informations au travers du bus AGP ou PCI jusqu'à la carte graphique. A la place, nous dessinons simplement dans la mémoire et ensuite expulsons le tout à la carte d'un seul coup. Bien plus efficace.
Biensur, avant de dessiner dans le back buffer, ce n'est pas une mauvaise idée de l'effacer, ainsi nous n'aurons pas de binz indésirable dans notre scène. L'appel réalisant cela a cette tête :
device.Clear(ClearFlags.Target, Color.Black, 1.0F, 0);
Remarquez que l'appel se fait sur l'objet Device créé lors de InitializeGraphics
Le premier argument de la methode indique à Direct3D ce que nous voulons effacer. Il y a plusieurs options, mais pour notre exemple simpliste, nous voulons juste effacer le back buffer, ainsi nous passons ClearFlags.Target afin d'indiquer que nous effaçons la cible de notre rendu.
Le deuxième paramètre indique avec quelle couleur nous désirons effacer la surface. Notez qu'on utilise ici une valeur provenant de l'énumeration System.Drawing.Color - une des choses sympas avec Managed DirectX est qu'on a la possibilité de travailler avec des types qui nous sont familiers, bien qu'il y ait toujours des cas où nous devons utiliser des entiers pour définir une couleur.
Laissons de côté les deux derniers arguments pour l'instant, mettre 1.0F et 0 fonctionnera dans la majorité des cas.
Nous avons besoin de prévenir Direct3D qu'on va commencer à dessiner afin qu'il s'organise de manière interne. Cela est tres facile, ça tient sur une seule ligne de code :
device.BeginScene();
Si vous commencez à vous apercevoir que Direct3D possède un nombre affreux de methodes sur l'objet Device, vous avez raison. Ce type de programmation à la "je manipule une variable globale avec 500 methodes" m'a pris au dépourvu au début - je préferais des appels genre Scene.Begin() plutot que device.BeginScene() - mais j'ai fini par m'y faire.
Ceci est la partie où nous dessinons des formes sur la scène : fusées, nail guns, spheres, pyramides, qu'importe. Là aussi nous allons sauter cette partie car il y a un paquet d'autres choses dont nous devons évoquer avant, mais ne vous inquietez pas - on va y être bientôt.
Celle la est facile. A un appel à BeginScene nous faisons correspondre un appel à EndScene, comme ceci :
device.EndScene();
Comme BeginScene, ceci indique à Direct3D de mettre à jour ses données internes.
Cette étape est en fait la seule qui fait des choses sur l'écran. En appelant
device.Present();
nous ordonnons à Direct3D qu'il prenne le contenu du back buffer et qu'il l'envoie éventuellement à la carte graphique qui repercuterait l'image sur le moniteur. Je dis éventuellement car il ya certains flags qu'on pourrait positionner et qui décaleraient la copie du back buffer mais améliorerait la qualité de notre animation. Par exemple, ce serait cohérent d'attendre que le balayage du moniteur passe du coin inferieur droit au coin superieur gauche. Cela s'effectue à une certaine fréquence et ça s'appelle le "vertical retrace". Copier des choses dans la carte graphique pendant que le moniteur fait cela peut éviter des désagrements visuels chelous.
Pour l'objectif de notre (déjà lointain) petit programme cependant, nous pouvons simplement assimilier la methode Present comme la partie qui envoie notre scène à l'écran.
Et voilà - une vraie application Direct3D! Ok ok, ça ne fait que peindre la fenêtre en noir, mais c'est une façon de faire méchamment efficace!
Ci-dessous le code complet utilisé dans cet article. La prochaine fois nous découvrirons les VertexBuffers dont nous aurons besoin pour dessiner quelquechose d'intéressant à l'écran.
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace Craig.Direct3D { public class Game : System.Windows.Forms.Form { private Device device; static void Main() { Game app = new Game(); app.InitializeGraphics(); app.Show(); while (app.Created) { app.Render(); Application.DoEvents(); } app.DisposeGraphics(); } protected bool InitializeGraphics() { PresentParameters pres = new PresentParameters(); pres.Windowed = true; pres.SwapEffect = SwapEffect.Discard; device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, pres); return true; } protected void Render() { // Paint la surface en noir partout device.Clear(ClearFlags.Target, Color.Black, 1.0F, 0); device.BeginScene(); // Appels aux méthodes de dessin ici device.EndScene(); device.Present(); } protected void DisposeGraphics() { device.Dispose(); } } }