Astuces DotNet (Sébastien Courtois)

11/12/2010

[XNA 4] Tutoriel 3D 4 : Modèles 3D et gestion de la lumière

Filed under: .NET, 3D, C#, Débutant, XBOX 360, XNA — Étiquettes : , , , , , , — sebastiencourtois @ 01:20

Lors du dernier article sur la 3D dans XNA, nous avons vu comment charger des données graphiques pour les afficher (exemple du cube). Rentrer l’ensemble des coordonnées des points à la main est fastidieux rien que pour dessiner un cube alors je vous laisse imaginer pour une maison ou un personnage. C’est pourquoi il existe des modeleurs 3D permettant de dessiner les modèles 3D puis de générer des fichiers contenant l’ensemble des données pré-formatés pour être utilisable par la carte graphique.

Il existe de nombreux modeleurs 3D. L’un des plus utilisé est Blender. Il offre une bonne partie des fonctionnalités que propose les modeleurs pro come 3DSMAX ou Maya et il est gratuit. Son seul inconvénient est d’être parfois complexe à utiliser (beaucoup de raccourcis clavier à connaitre).

Les modeleurs 3D peuvent générer des fichiers contenant les informations 3D de la scène dessiné (position des vertex, indices, triangles, textures ….). Il existe un très grand nombre de format dans l’univers de la 3D (certains éditeurs de jeu crée parfois leur propre format pour un moteur de jeu défini). XNA prend en charge nativement au moins deux formats : le .X (format DirectX) et le format .fbx (Format Autodesk).

Dans ce tutoriel, nous utiliserons 3 modèles : Une sphère exporté depuis Blender (fait par moi même), un personnage texturé (venant du SDK DirectX) et un tank en 3D venant des démos XNA disponible sur le site officiel.

  • Chargement d’un modèle 3D

La première étape est de charger les modèles ainsi que leurs textures dans le projet ‘Content’ de la solution.

blog_model_1

(sphere.fbx correspond à la sphère / tiny.x et tiny_skin.dds correspond au personnage et tank.fbx,engine_diff_tex.tga et turret_alt_diff_tex.tga correspondent au tank)

Ensuite il suffit de charger les données dans le ContentManager de XNA. Un modèle 3D se charge dans la classe Model. Le chargement se réalise grâce à la méthode Load du ContentManager.

Model sphere;
Model tiny;
Model tank;

protected override void LoadContent()
{
    spriteBatch = new SpriteBatch(GraphicsDevice);

    this.sphere = Content.Load<Model>("sphere");
    this.tiny = Content.Load<Model>("tiny");
    this.tank = Content.Load<Model>("tank");

    this.font = Content.Load<SpriteFont>("font");
}
  • Dessin d’un modèle 3D

Une fois chargé, on souhaite afficher le modèle. Cela se réalise dans la méthode Draw de la classe Game. On peut faire afficher un modèle de plusieurs manières.

Tout d’abord, on peut afficher le modèle dans sa totalité en appelant la méthode Draw du Model :

this.sphere.Draw(Matrix.Identity, this.View, this.Projection);

La méthode Draw demande 3 paramètres correspondant aux 3 matrices principales que l’on à déjà vu dans les tutoriaux précédent (World, View, Projection). La plus importante des trois est la première (World) car elle permet d’indiquer la position, l’orientation et la taille de l’objet à afficher et varie donc généralement d’un modèle à l’autre.

Une autre méthode revient à afficher le modèle morceaux par morceaux. En effet, si vous regarder la classe Model, elle est composé de plusieurs ModelMesh eux mêmes composé de MeshPart. Dans chacun de ces objets se trouve un objet Effect (BasicEffect la plupart du temps) qui va interagir avec ces données. On peut donc afficher un modèle de la façon suivante :

foreach (ModelMesh mesh in this.tank.Meshes)
    foreach (BasicEffect effect in mesh.Effects)
    {
        effect.World = Matrix.Identity;
        effect.View = this.View;
        effect.Projection = this.Projection;
        mesh.Draw();
    }

Ce moyen peut paraitre plus long et compliqué mais il peut avoir son utilité notamment lors des animations de modèles que nous verrons dans un prochain tutoriel.

blog_model_2

  • La lumière : Les bases

Maintenant que vous savez charger des modèles 3D, nous allons nous intéresser à la lumière.

Si on se réfère à la définition physique Wikipedia, la lumière est une onde électromagnétique visible par l’œil humain. Sa longueur d’onde va de 380nm (violet) à 780 nm (rouge). La lumière a généralement une source qui peut être plus ou moins éloigné des objets éclairés. Lorsque la lumière part de cette source, elle se propage dans toutes les directions. Lorsqu’elle touche un objet, cet objet absorbe une partie des longueurs d’onde  et réfléchi le reste. La lumière réfléchie arrive dans notre œil. Si l’on regarde une balle rouge, cela veut dire que la balle a absorbé toute les autres longueurs d’onde que le rouge.

De plus chaque face d’un objet à une normale. Il s’agit d’un vecteur perpendiculaire à la  surface. Lorsqu’un rayon lumineux “touche” une face d’un objet selon un angle (angle d’incidence) par rapport à la normale de cette face, ce rayon est réfléchi avec le même angle à l’opposé de cette normale.

Afin d’avoir des rendus lumineux réalistes, XNA (et les API 3D en général) s’inspire très fortement des phénomènes physiques décrits ci-dessus.

  • Gestion de la lumière dans XNA : Pratique

Après ces rappels physiques, nous allons maintenant passer à l’utilisation de la lumière en XNA. Pour faire simple, nous utiliserons les lumières disponibles au travers de l’effet BasicEffect fourni par XNA. Les lumières sont gérés dans les effets car ce sont des opérations parfois couteuses en temps et que les effets sont exécutés par le GPU (donc plus rapidement). Nous verrons dans les tutoriaux sur les shaders comment créer nos propres types de lumières.

Par défaut, les lumières sont désactivés. On voit alors les modèles selon leurs couleurs originales (textures/couleurs définis directement dans le modèle). Pour l’activer, il suffit de passer la propriété LightingEnabled de BasicEffect à true.

blog_model_3

Notre modèle apparait alors en noir. Cela est normal car, bien que l’objet soit texturé, aucun rayon lumineux ne se réfléchi sur le tank donc notre camera (notre œil virtuel) ne reçoit aucune lumière donc il voit l’objet en noir.

  • Nous allons activer une première composante lumineuse : La composante ambiante.

Cette lumière est une lumière sans source lumineuse défini et dont la lumière est présente partout. Pour faire une analogie, on pourrait comparer cela à un jour nuageux au travers desquels ont ne voit pas la lumière. La lumière est pourtant présente car on parvient à voir les objets nous entourant. Par défaut, la lumière d’ambiante est noire. Il est possible de changer cette valeur grâce à la propriété AmbientLightColor.

blog_model_ambient

En terme de calcul, cela est très rapide car il suffit de rajouter la couleur ambiante sur les pixels du modèle sans tenir compte “des données physiques “(normales notamment). Le soucis est que cela rend le modèle plat.

  • Deuxième type de composante lumineuse: La composante diffuse

A l’instar de la lumière ambiante, la lumière diffuse ne provient d’aucune source définie. Toutefois, le calcul de la lumière prend en compte les normales de chacune des faces. Ainsi l’affichage fera apparaitre des ombres selon l’angle sous lequel l’objet est regardé. Il est possible de modifier la couleur de la lumière diffuse avec la propriété DiffuseColor de BasicEffect.

blog_model_diffuse

  • Troisième type de composante lumineuse : La composante spéculaire

La lumière spéculaire est le léger reflet que l’on voit dans un objet réfléchissant. Elle dépend de la position de la caméra, de la source lumineuse et différents vecteurs incidences/réflexions de la lumière. Il est possible de définir la force de la tache spéculaire ainsi que sa couleur avec les propriétés SpecularPower et SpecularColor.

blog_model_specular

Ici, la lumière est blanche (diffuse) et la lumière spéculaire est jaune.

  • Dernier type de composante lumineuse : La composante émissive

Cette composante va simuler le fait que le modèle émettent de la lumière. On peut définir la couleur d’émission avec la propriété EmissiveColor de BasicEffect.

blog_model_emissive

Exemple avec une lumière émissive bleu.

Si vous voulez plus d’informations sur les lumières en 3D, je vous conseille cet article.

  • Une lumière réaliste : La lumière directionnelle

BasicEffect permet la création de 3 lumières directionnelle. Une lumière directionnelle n’a pas de source et n’est défini que par ses composants et un vecteur directeur. On peut envisager cela comme étant une source lumineuse se trouvant à l’infini dont l’ensemble des rayons arrivent sur l’objet parallèle les uns aux autres selon le vecteur directeur.

blog_model_noDL blog_model_DL
Modèle sans lumières Modèle avec une lumière directionnelle blanche.

Le calcul de la lumière se faisant en fonction du vecteur directeur et des différentes normales des surfaces, on obtient un rendu plus réalistes avec des ombres et des tâches spéculaires.

Les propriétés des lumières directionnelles sont disponible avec la propriétés DirectionalLightX (avec X étant 0, 1 ou 2).

Exemple :

ef.DirectionalLight0.Enabled = DirectLightOn;
ef.DirectionalLight0.Direction = this.DirectLightPosition;
ef.DirectionalLight0.DiffuseColor = this.DirectLightColor.ToVector3();
ef.DirectionalLight0.SpecularColor = this.SpecularLight.ToVector3();
  • Méthode de calcul de la lumière : Par vertex ou par Pixel

Il est possible de calculer la lumière de deux façons. En calculant la lumière en fonction de chacun des vertex ou en faisant la calcul pour chacun des pixels. En général, le rendu par pixel est plus réaliste mais parfois plus lent que le traitement par vertex. Cela dépend de la définition des modèles. Plus il y aura de vertex sur le modèle, plus la qualité du traitement par vertex sera lourd et réaliste.

blog_model_noDL2 blog_model_DL2 blog_model_DL2_PL
Modèle normal Lumière calculée par vertex Lumière calculé par pixel
  • Brouillard

BasicEffect permet aussi de faire des sortes de brouillard. Les brouillards sont souvent des astuces dans les jeux vidéos pour réduire la profondeur de champ et de ne pas à avoir à afficher les objets 3D loin de la caméra. Le brouillard comporte 4 propriétés. La propriété FogEnabled permet d’activer ou désactiver le brouillard. FogColor permet de définir la couleur du brouillard. On peut définir la distance où commence et ou se termine le brouillard. Les propriétés  FogStart et FogEnd sont des valeurs numériques définissant la distance par rapport à la caméra.

blog_model_fog

  • La démo

J’ai réalisé une démo afin de pouvoir réaliser l’ensemble des screenshots pour ce tutoriel. Il est pilotable au clavier et permet de modifier l’ensemble des propriétés présentées ici.

La démo est disponible ici (11 Mo)

Raccourcis clavier :

Flèches directionnelles : Déplace la caméra autour de l’objet

Page Up/Page Down: Zoom +/-

A /Z/E: Changer le modèle (A : Sphère / Z : Personnage / E : Tank)

Q : Activation/Désactivation des lumières

S : Change le mode de calcul de la lumière (Vertex/Pixel)

D : Activation/Désactivation de la lumière directionnelle 0

Q : Change la couleur de la composante ambiante

H : Change la couleur de la composante diffuse

J : Change la couleur de la composante d’émission

R : Change la couleur de la composante spéculaire

T/Y : Augmente ou réduit la force de la composante spéculaire

W : Activation/Désactivation du brouillard

X : Change la couleur du brouillard

C/V : Change la valeur FogStart

B/N : Change la valeur de FogEnd

K/L/M/O : Déplacement de la lumière directionnelle 0 autour de l’objet

  • Conclusion

Nous avons fait un bon tour sur les lumières. Nous avons aussi exploré BasicEffect, l’effet de base fourni par XNA. Nous verrons, dans le prochain tutoriel, comment créer son propre effet en HLSL ainsi que la façon dont on peut réaliser des animations sur des modèles chargés dans XNA.

Publicités

Propulsé par WordPress.com.

%d blogueurs aiment cette page :