XNA Tutorial 3 : Introduction aux vertices et effets

Retourner au sommaire des cours  


Il est maintenant temps d’afficher quelque chose à l’écran.


Le but de ce tutorial est de nous apprendre à dessiner à l’écran en 3D à l’aide de Vertices, et de commencer à assimiler la notion d’effet. Il s’agit là d’un cours primordial dans la mesure où un développeur de jeux XNA  aura toujours à faire à ces deux notions, et ce, dans n’importe quel projet qu’il réalisera. Il convient donc de bien assimiler ce que nous allons réaliser ici (où bien de me contacter le cas échéant). Nous allons réaliser ici l’affichage d’un triangle coloré.


Le vertex


 Si en 2D afficher un objet revient à “plaquer” une image à l’écran, en 3D la done est tout autre. Les objets ne sont pas dessinées à partir d’une image mais plutot a partir de vertices (un vertex, des vertices). Un vertex peut être vu comme un point dans l’espace auquel on associe des propriétés (couleur, position …). Le pipeline 3D va relier ces points pour former un objet. Le développeur a pour tache de bien placer ces points et d’indiquer au device comment les relier.


L’image suivante illustre cela. Nous voyons un tube et un cube en trois dimension. On remarque qu’ils sont tout simplement formés à partir de points qui sont reliés entre eux.



La forme géométrique la plus simple en 3D est le triangle. Trois vertices sont nécessaires pour la former. Toute forme géométrique en 3D est formée de triangles. C’est à dire d’une multitude de vertices qui, reliés, permettent de donner une apparence à certains objets. La encore, l’image ci dessus le montre très bien.


Plusieurs solutions existent pour relier les vertices entre eux afin de former des objets 3D. Chaque solution possède ses propres avantages en fonction la forme de l’objet . C’est pourquoi il convient d’utiliser le celle qui s’avère la plus adapté dans un seul but : réduire au maximum le nombre de vertices. L’association de plusieurs vertices entre eux permet à XNA de former des triangles (assimilables à des surfaces). L’association de ces surfaces permet alors de former des objets dans l’espace. XNA résonne en ternaire ; Il lui faut trois point pour former la plus petit figure géométrique possible : le triangle. 


Lorsqu’on place des points dans l’espace il est donc nécessaire de spécifier à la carte graphique la manière dont il faut relier ces points afin de créer une forme 3D. Les différentes manières possibles sont les suivantes :


Triangle List
Le mode de liaison de vertex le plus simple. Celui-ci lit les points trois par trois afin de former des triangles qu’il affiche. On comprendra donc il est nécessaire de créer un nombre de vertices multiple de trois. Les trois premiers vertices représenteront le premier triangle, les trois suivant le second triangle et ainsi de suite… C’est le mode de définition le plus simple …  mais aussi le plus coûteux.


Triangle Fan
Ce mode (comme le suivant) est plus intelligent ; il réutilise des vertices déjà utilisé pour les liaisons. On gagne ainsi de précieux octets en mémoire libérant ainsi le pipeline 3D. Dans ce mode le premier vertex est relié à tous les autres. Là aussi, les possibilités sont limités ; on ne peut faire que des formes rectangulaires et/ou cylindrique comme le montre le schéma ci-contre.


Triangle Strip
Ce troisième mode est un chouïa plus compliqué. On relie ici aussi les vertices par trois. Mais en considérant que le dernier vertex créé doit être relié aux deux précédents. Le gain de place en mémoire est très important, mais la difficulté de créer des formes complexes est multipliée.


Primitive indexée
Nous verrons dans quelques pages la création de primitives indexées. Ce mode de liaison de vertices est la solution à tous les problèmes précédemment listés pour créer des formes complexes en créant un minimum de vertices.


L’image ci-dessus tente de montrer les différences que l’on peut trouver avec les trois premier modes pour l’affichage de triangles :



a) Triangle List :
3 triangles : Vertices (0, 1, 2) puis Vertices (3, 4, 5), puis Vertices (6 7, 8) : 9 vertices pour afficher 3 triangles.
b) Triangle Fan :
3 triangles : Vertices (0, 1, 2) puis Vertices (0, 2, 3), puis Vertices (0, 3, 4) : 5 vertices pour afficher 3 triangles.
c) Triangle Strip :
6 triangles : Vertices (0, 1, 2) puis Vertices (1, 2, 3), puis Vertices (2, 3, 4), puis Vertices (3, 4, 5), puis Vertices (4, 5, 6), puis Vertices (5, 6, 7) : 8 vertices pour afficher 6 triangles.


Au vu de cette image on remarque bien les différences entre les types de reliaisons. D’autres reliaisons sont possible :


Point qui ne fait aucune liaison et laisse les points tels quels (très utile pour les effets de particules),


LineList qui relies les points deux à deux (utile pour dessiner des lignes)


LineStrip qui relie un point à son prédécesseur.


En C#, c’est l’énumération PrimitiveType qui contient les différents types d’énumérations possibles.


Rappelons que nous avons pour but d’afficher un triangle dans ce tutorial, il faudra donc créer 3 vertices, leur donner une position et, au moment de l’affichage spécifier la meilleure méthode de liaison.


Les effets


La première grande différence entre l’API DirectX et le Framework XNA auquel on est confronté vient de l’obligation d’associer un effet à toute opération de rendu. Si vous n’avez jamais développé à l’aide de DirectX vous devez vous demander ce qu’est un effet.


Nous venons de voir que les objets sont présentés sous la formes de triangles (surface élémentaire). Nous spécifions par l’intermédiaire de notre code la position des points du triangle et d’autres informations comme la couleur de ces points. Un effet accompagne ces informations en indiquant à la carte graphique comment afficher ces triangles. Il peut être vu comme un ensemble de technique qui peuvent être appliquées (ou non) au rendu d’une surface. Par techniques on peut entendre instructions données à la carte graphique pour lui “expliquer” de quel manière l’objet, situé dans un espace 3D, doit être rendu sur l’écran 2D du joueur. 


Il s’agit là, il ne faut pas se le cacher, d’une technique encore trop complexe pour être abordée à ce stade de notre apprentissage. Nous utiliserons donc un fichier effet basique fourni par Microsoft et nous ne l’aborderons que succintement.


 Modification du code source


Nous allons continuer sur notre lancée et modifier notre précédent programme nommé “PremierProjet”. Nous appliquerons au projet de cet article nommé à juste titre “SecondProjet” les modifications et evolutions apportées dans le précédent tutorial.


La première étape consiste à ajouter un fichier effet au projet et à le gérer dans le code.


Pour ce faire cliquez droit sur le répertoire Content du projet (destiné à gérer les ressources du jeu). Selectionnez Ajouter -> Nouvel Element.



Dans la fenêtre qui s’ouvre alors selectionnez Effect File et donnez le nom Effect.fx au fichier à ajouter :



Validez. Le fichier se trouve alors ajouté au répertoire Content.


Si vous double cliquez dessus vous pourrez voir le code suivant :


float4x4 World;
float4x4 View;
float4x4 Projection;
 
// TODO: add effect parameters here.
 
struct VertexShaderInput
{
    float4 Position : POSITION0;
 
    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};
 
struct VertexShaderOutput
{
    float4 Position : POSITION0;
    // TODO: add vertex shader outputs such as colors and texture
    // coordinates here. These values will automatically be interpolated
    // over the triangle, and provided as input to your pixel shader.
};
 
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
 
    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
 
    // TODO: add your vertex shader code here.
 
    return output;
}
 
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    // TODO: add your pixel shader code here.
 
    return float4(1, 0, 0, 1);
}
 

technique Technique1
{
    pass Pass1
    {
        // TODO: set renderstates here.
 
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_1_1 PixelShaderFunction();
    }
}
 
 
Nous reviendrons sur sa signification plus tard. Pour l’heure nous allons charger l’effet en mémoire à l’intérieur d’un object C# pour pouvoir l’utiliser. Ajoutez pour celà le membre suivant à la classe Game1 :


Effect effect;


Ajoutez l’instruction suivante à la méthode Load de la classe Game1 :


this.effect = Content.Load<Effect>(“Effect”);


Cette instruction va utiliser la méthode Load de l’objet content pour charger la ressource nommée “Effect”. La ressource renvoyée sera de type Effect et affectée au handle effect. Pour rappel, il est spécifié par défaut dans le constructeur que le répertoire où trouver les ressources est Content :


Content.RootDirectory = “Content”;


L’objet Content, utilisé dans l’instruction précédente correspond au Content Manager, c’est à dire un objet dont le rôle est de gérer les ressources associées au projet. Sa méthode Load permet de charger une ressource dans un objet dont les membres vous nous permettre d’exploiter la dite ressource dans notre code. Ici on peut voir que notre fichier d’effet est manipulé maintenant par l’intermédiaire d’une instance de la classe Effect.


La string “Effect” est un identifier de ressource qui a été automatiquement associé à notre effet lorsque nous l’avons ajouté au projet. Selectionnez le fichier Effect.fx et appuyez sur F4. La fenêtre de propriété apparait alors :



Vous pouvez y voir que l’une des propriété se nomme AssetName et correspond à cette string. Vous êtes bien evidemment libre de modifier cette propriété pour lui donner la valeur de votre choix. Toutefois il est préférable de laisser le Xna Game Studio et Visual Studio effectuer ces choix à votre place.


Modifiez ensuite la méthode Draw pour lui ajouter de nouvelles instructions :


this.effect.CurrentTechnique = effect.Techniques["Technique1"];
this
.effect.Begin();
foreach (EffectPass pass in this
.effect.CurrentTechnique.Passes)
{
    pass.Begin();

    pass.End();
}
this.effect.End();


La première instruction sélectionne une technique nommée “Technique1″ parmi celles contenues dans l’effet. Nous utilisons cet identifier tel quel en tant qu’index dans le tableau de technique. Notons juste qu’il s’agit là d’un code très mauvais ; il aurait été préférable d’utiliser une constante de type int. Trouver un objet à l’aide d’un indexeur de type string est toujours plus long qu’avec un indexeur de type int (la comparaison de deux int est plus rapide que deux strings). Or perdre du temps dans la méthode Draw est vraiment la dernière chose à faire. Il y’a une explication à celà : je trouve le code plus lisible ainsi pour les débutants :).


Alors qu’est ce qu’une technique ? Un fichier effet peut contenir une ou plusieurs technique qui s’apparentent à un ensemble d’affichage possible pour une forme 3D.L’appel à la méthode Begin indique que l’effet de la technique “Technique1″ commence à partir de ce point et s’applique à l’affichage de tous les objets jusqu’à ce que la méthode End soit rencontrée. Entre ceux deux méthodes tout affichage sera soumi à la technique “Technique1″.


Enfin, on a jamais vu quelqu’un peindre un chef d’oeuvre d’un seul coup de pinceau ; Appliquer un effet peut demander plusieurs couches ou “passes”. Là encore une méthode Begin et End indiquent la zone de code où l’effet de la passe courante est pris en compte. C’est entre ces deux méthodes que nous effectuerons l’affichage de notre triangle. Vous pouvez au passage revenir sur le code contenu dans le fichier Effect.fx. Vous trouverez à la fin de celui-ci :


technique Technique1
{
    pass Pass1
    {
        // TODO: set renderstates here.
 
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_1_1 PixelShaderFunction();
    }
}


On y trouve la définition de la technique  et on peut voir qu’elle se compose d’une passe redirigeant vers deux fonctions. Nous aborderons le HLSL plus tard, ne vous inquietez pas si le code de ce fichier vous parait obscur.


Nous sommes justement maintenant prets à afficher notre première forme 3D !


Première forme 3D !


Nous avons vu au tout début de ce cours qu’un vertex était un point dans l’espace auquel on associait un ensemble d’information liées à son affichage dans l’espace. Le but de ce tutorial est d’afficher un triangle coloré à l’écran. Chaque point dans l’espace (vertex) doit donc être associé à une position et un couleur donc. Cerise sur le gateau nous n’aurons pas besoin de créer une structure de Vertex pour encapsuler ces deux informations : XNA en possède une qui répond à nos besoins : la structure VertexPositionColor.


Un triangle est composé de trois points, il faut donc créer un tableau de trois cases de type VertexPositionColor. Ajoutez la déclaration suivante en début de classe :


 VertexPositionColor[] vertices;


Puis, dans la méthode Initialize nous allons créer trois vertices et l’affecter aux trois cases de ce tableau.


vertices = new VertexPositionColor[3];
vertices[2].Position =
new Vector3
(-0.5f, -0.5f, 0f);
vertices[2].Color =
Color
.Red;
vertices[1].Position =
new Vector3
(0, 0.5f, 0f);
vertices[1].Color =
Color
.Green;
vertices[0].Position =
new Vector3
(0.5f, -0.5f, 0f);
vertices[0].Color = Color.Yellow;


Ici le code est plutot simple : trois vertices sont créés. Chacun deux se voit affecter une position et une couleur. Nous sommes dans l’espace 3D. Une position correspond donc à trois composantes X, Y, Z (repère euclidien orthonormé dans l’espace). Le constructeur de Vector3 accepte en paramètre ces trois composantes.Reste maintenant l’affichage dudit triangle. La méthode Draw possède deux nouvelles instructions.


this.graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(this.graphics.GraphicsDevice, VertexPositionColor.VertexElements);
this.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, 1);
 


Dans la première ins truction nous précisions la structure des vertices qui sont passés au device graphique. la structure VertexPositionColor  contient un membre statique nommé VertexElements qui précise que la structure contient une information de position, et une information de couleur. De cette manière, la carte graphique optimise la gestion mémoire en vue de l’arrivée dans le pipeline d’un flux de vertices contenant ce type de données.


Dans la seconde instruction justement, nous passons à la carte graphique une instruction de dessin en précisant, la manière dont on va relier les vertices, le flux de vertices, l’index du premier vertex à lire dans le flux et le nombre de primitives (triangles) à afficher. Ici nous utilisons la technique TriangleList, nous donnons un flux de trois vertices (le tableau que nous venons d’initialiser), nous lisons le flux à partir du premier vertex, et nous n’affichons qu’un seul triangle.


Si vous lancez le code vous obtenez la fenêtre :



A priori on s’attendait à avoir un triangle affiché et non un écran bleu. Il est en effet nécessairede spécifier la façon dont on veut afficher la scène 3D à l’écran.


L’affichage d’un objet 3D à l’écran se réalise généralement par l’intermédiaire de trois matrices. Une matrice est une « boite magique » mathématique qui permet de réaliser des équations linéaires avec peu de traitements. Elles sont utilisées en 3D pour réaliser de nombreux calculs fastidieux de manière extremement rapide et efficaces. Ces trois matrices sont les suivantes :


1.   La première matrice est la matrice World, elle spécifie où placer un objet dans un repère 3D (translation), son orientation (rotation), et sa taille (homothetie).


2.   La matrice View précise la façon dont est « filmé » la scène. On parle alors de caméra.


3.   La troisième matrice, nommée Projection donne les caractéristiques de la caméra, comme l’angle d’ouverture, la taille de l’écran, etc.


Ajoutez ces trois instructions à la méthode Draw, juste avant le premier appel à Begin :

this.effect.Parameters["World"].SetValue(Matrix.Identity);
this.effect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0, 0, -2), Vector3.Zero, Vector3.Up));
this.effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.GraphicsDevice.Viewport.AspectRatio, 0.1f, 100f));

La propriété Parameters de la classe Effect permet d’accéder aux variables présentes dans le fichier effet et de leur affecter une valeur ou bien de la lire. Le fichier effet expose, comme on va le voir bientôt trois variables, World, View et Projection. Nous leur donnons ici, à chacune, la valeur d’une matrice.


Pour la matrice World, nous indiquons qu’il n’y aura pas de transformation, la valeur Identité de la matrice (matrice n’effectuant aucun calcul) est est ainsi passé à cette variable.


Pour la matrice View nous utilisons la méthode CreateLookAt de la classe Matrix afin de préciser la position de la caméra (ici la caméra est simplement reculée de deux unités en profondeur), l’endroit vers lequel on regarde (ici on regarde vers l’origine (0, 0, 0) ) et l’angle de la caméra avec l’horizontale (ici angle droit).


Enfin la matrice Projection spécifie un angle d’ouverture de 45° (PI/4), le ratio (largeur de l’écran sur sa hauteur), et enfin deux valeurs importantes : la distance à la caméra à partir de la quelle on peut voir un objet, et la distance au delà de laquelle un objet n’est plus visible (front plane et far plane). Là encore une méthode de la classe Matrix nommée CreatePerspectiveFieldOfView permet de créer une matrice facilement. Le ratio permet de garder un aspect cohérent pour notre scène en cours d’affichage quelque soit la taille de la zone d’affichage.


Ces trois matrices sont multipliées entre elles afin de passer d’une scène 3D à un écran 2D. C’est la carte graphique qui s’occupe de celà. On peut donc voir ces calculs comme le moyen de passer d’une position dans l’espace (X, Y Z) avec des propriétés de couleur, de texturing, d’illumination à une position à l’écran (X, Y) d’un pixel coloré.


 Si vous relancez le programme maintenant vous obtenez l’écran suivant :



Mieux, mais toujours insuffisant ! A priori nous devions avoir un triangle avec plusieurs couleurs, or là, seul le rouge est visible. Il va nous falloir, pour aboutir à ce résultat, étudier et comprendre le code du fichier effet afin de pouvoir le modifier.


Courage ! Ouvrez donc maintenant le fichier effect.fx en double cliquant dessus. Vous devriez tomber sur le code précédemment affiché dans cet article. A première vue le code semble assez proche du language C. C’est enfait du HLSL (High Level Shader Language). Il est donc assez “lisible”, mais nous allons tout de même l’expliciter. Etudions le.


Au tout début du fichier se trouve les déclarations de variables globales du fichier effet. On y retrouve justement les trois déclarations de matrices que nous avons affecté précédemment :


float4x4 World;
float4x4 View;
float4x4 Projection;


On affecte des valeurs ou on récupere la valeur d’une de ces variables à l’aide de la propriété Parameters de l’objet effect :


this.effect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(0, 0, -2), Vector3.Zero, Vector3.Up));


Passons à la suite du fichier. On trouve après ces trois variables la déclaration de deux structures :


struct VertexShaderInput
{
    float4 Position : POSITION0;
 
    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};
 
struct VertexShaderOutput
{
    float4 Position : POSITION0;
    // TODO: add vertex shader outputs such as colors and texture
    // coordinates here. These values will automatically be interpolated
    // over the triangle, and provided as input to your pixel shader.
};


Généralement, un effet réalise l’affichage d’un objet auquel il est lié en deux étapes. La première étape, consiste à manipuler les vertices de l’objet à leur appliquer une suite de transformations et de calculs afin de pouvoir passer de la représentation 3D de l’objet à un écran 2D. Cette étape correspond à ce qu’on appelle le vertex shader. Dans une seconde étape, nommée Pixel Shader chaque triangle de la scène est convertie en pixels et chaque pixels est coloré à sa juste valeur. A priori, si notre problème vient de la couleur du triangle, c’est sur le pixel shader qu’il va faloir travailler. A quoi servent donc les deux structures ci-dessus ? Elle permettent de réaliser plusieurs types d’échanges de données :


  • D’abord entre l’extérieur (notre programme C#) vers le premier shader de l’effet (le vertex shader)
  • Puis entre le vertex shader et le pixel shader
  • Et enfin entre le pixel shader et la carte graphique pour l’affichage final.

La première structure, nommée à juste titre VertexShaderInput permet à l’effet de recevoir tous les vertices que nous avons passé à l’aide de la méthode DrawUserPrimitives. Il ne recevra ici qu’une information de Position :


struct VertexShaderInput
{
    float4 Position : POSITION0;
 
    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};
 


On remarque donc ici un premier problème : il n’y a pas de membre pour stocker la couleur du vertex ! La seconde struture nommée VertexShaderOutput est la structure de donnée échangée entre le Vertex Shader et le Pixel Shader.


Un shader dans un effet peut donc être vu comme une fonction qui, renvoie ou reçoit une structure (comme celles que nous venons de décrire) effectue des traitement sur les données de celle-ci, et renvoie une autre structure de données contenant le resultat de ces traitements.


Etudions la première fonction : 
 
VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
 
    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
 
    // TODO: add your vertex shader code here.
 
    return output;
}


Elle prend en entrée une valeur de type VertexShaderInput et renvoie une valeur de type VertexShaderOutput. La première instruction de la fonction vise à créer une variable nommée ouput de type VertexShaderOutput. Elle multiplie ensuite à l’aide la méthode mul la position du vertex courant (reçu directement depuis le flux envoyé par la méthode Draw) par la matrice Word afin de placer le vertex dans le monde 3D. Elle multiplie ensuite le resultat de cette opération par la matrice View afin d’avoir la représentation du vertex dans l’espace depuis notre “camera”. Enfin une multiplication est réalisée avec la matrice Projection pour passer du monde 3D à l’écran 2D. La valeur obtenue est affecté à un des membre de la variable ouput qui est elle-même renvoyée.


La seconde méthode se présente comme ceci :


float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    // TODO: add your pixel shader code here.
 
    return float4(1, 0, 0, 1);
}


Elle prend une valeur de type VertexShaderOuput (reçue depuis la fonction précédente) et renvoie un tableau de 4 flottants. Nous reviendrons sur la signification exacte du type float4 plus tard. Quoi qu’il en soit à ce stade nous pouvons dire que les 4 flottants correspondent aux composantes RGBA (rouge, vert, bleu, alpha) de la couleur à attribuer au pixel. 1 correspondant à la valeur maximale de la composante et 0 à la minimale. Ici la couleur renvoyée est donc le rouge. On remarque donc ici un second problème : tout pixel sera rouge !


Enfin le fichier se termine par la définition de la technique :


technique Technique1
{
    pass Pass1
    {
        // TODO: set renderstates here.
 
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_1_1 PixelShaderFunction();
    }
}


Le fichier ne contient donc qu’une seule technique nommée Technique1. Celle-ci réalise son affichage en une seul passe nommée Pass1.


La passe se caractérise par un vertex shader et un pixel shader. On retrouve un pointeur vers nos deux méthodes. Les mots clés “compile vs_1_1″  et “compile ps_1_1″ indiquent que les shaders doivent être compilés dans la version 1.1 du language.


Pas d’inquiétudes s’il reste des zones d’ombre, nous avons parcouru le fichier très rapidement sans nous attader sur certaines spécificités syntaxiques. Revenons à notre problème : le triangle rouge.
Premier soucis, la structure en entrée ne contient qu’une information de Position. Or la structure que nous manipulons dans le programme C# contient Position et Couleur. Modifiez la structure ainsi :


struct VertexShaderInput
{
    float4 Position : POSITION0;
    float4 Color        : COLOR0;
 
    // TODO: add input channels such as texture
    // coordinates and vertex colors here.
};

Nous avons rajouté un nouveau champs nommé Color de type COLOR0. Réalisez la même opération avec la structure de sortie :


struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Color        : COLOR0;
 
    // TODO: add vertex shader outputs such as colors and texture
    // coordinates here. These values will automatically be interpolated
    // over the triangle, and provided as input to your pixel shader.
};

Reste maintenant à modifier les shaders. Commencez par le vertex shader. Nous avons juste ici à ajouter une ligne d’affection pour récupérer la couleur du vertex entrant et la passer à la valeur envoyée en sortie de méthode :


VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
 
    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
    output.Color = input.Color;
     // TODO: add your vertex shader code here.
 
    return output;
}

Reste le pixel shader. Nous n’avons plus à renvoyer la couleur rouge mais plutôt à renvoyer celle de la valeur en entrée qui est la sortie de la méthode précédente :


float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    // TODO: add your pixel shader code here.
 
    return input.Color;
}


Relancez le programme. Nous obtenons enfin la sortie voulue :




Conclusion


De nombreuses choses auraient pu être vues ici : les effets plus en profondeurs, un peu de mathématiques 3D, les différentes méthodes d’affichage du Device… Tout cela sera en fait assimilé au fil des tutoriaux. Pas d’inquiétudes à avoir donc. Pour l’heure nous pouvons être fier : nous savons créer un effet, l’appliquer. Nous sommes capable de créer une forme et de l’afficher (avec couleurs s’il vous plait !). Comme exercice en attendant de lire le prochain tutorial, essayez d’afficher un carré et une maison. Essayez de même de reproduire le carré avec les méthodes TriangleFan et TriangleStrip.


Pas d’inquiétude à avoir si il vous reste de nombreuses zones d’ombre sur les effets et le code qu’ils contiennent, nous apprendrons au cours d’un long tutorial à maitriser le HLSL.


 Vous pouvez télécharger le sample et les correction des exercices ici. 


Retourner au sommaire des cours 


 


 


 


 

36 thoughts on “XNA Tutorial 3 : Introduction aux vertices et effets”

  1. Bonjour,

    Je tiens tout d’abord à vous féliciter pour votre très bon tutoriel.
    Ayant réussi à faire un carré et une maison grâce à l’utilisation de Triangle List. J’aimerai avoir des solutions utilisant Triangle Fan ou Triangle Strip.

    De plus, je m’interroge au sujet du fichier effects.fx. En effet, ce fichier n’est pas présent lors de la création du projet et le tutoriel n’en parle pas. De ce fait, j’ai fait un copier-coller pour créer l’application.

    Enfin, je me suis aperçu que l’ordre des points pour chaque triangle avait son importance. Pourriez-vous détailler ce fait?

    Merci d’avoir pris le temps de lire mon mail.

    Cordialement,
    Frédéric

  2. Bonjour Frédéric, je me suis permi de mettre votre mail ici car il est interessant :)

    Pour triangleFan et TriangleList je vais voir pour créer un projet dans l’après midi rapidement pour le carré.

    Pour le fichier effet c’est une omission de ma part, j’en parle dans ce tutorial mais je n’ai pas précisé que je le copiais simplement dans le répertoire de l’exécutable (bin\x86\Debug). Ce fichier est un fichier standard tel qu’on en trouve dans le sdk XNA ou sur le net. Sa lecture étant compliquée je ne me suis pas attardé dessus. Je modifie le tutorial de ce pas.

    Oui l’odre a une importance. Je vous invite à regarder le tutorial 4 : http://msmvps.com/blogs/valentin/archive/2007/01/14/xna-tutorial-4-les-matrices.aspx rendez vous dans le paragraphe nommé “Backface Culling”. C’est par l’ordre de création des vertices que l’API XNA sait si une face se trouve face à la caméra. Lorsqu’elle se trouve en face de la caméra, on lit les vertices qui forment le triangle dans le sens des aiguilles d’une montre. Maintenant si on pivote la caméra pour la placer de l’autre coté, les mêmes vertices seront lu dans le sens inverse. Sachant cela, XNA est capable de dire si une face est visible ou non, on appelle cela le backface culling. Ce culling est paramétrable via la propriété CullMode. Il existe d’autre type de culling : le frustum culling : ne pas tenter d’afficher les objets qui ne sont pas dans le champs de la caméra, l’occlusion culling : ne pas afficher les objets qui sont cachés par d’autres objets plus proche de la caméra, et le distance culling, ne pas afficher les objets dont la taille est trop petite du fait de leur distance face à la caméra. Mais sur tout ce la nous reviendrons dans le tutorial euh … 24 :)

  3. En lisant ce tutorial, je me suis dis que je pourrais l’utiliser pour dessiner des rectangles de couleurs sur l’écran. Le XNA manque de fonctions 2D pour dessiner des formes simples comme des points, des rectangles, des lignes, etc.. C’est utile pour dessiner des éléments d’interfaces.

    J’ai tenté de modifier l’exemple pour afficher le triangle avec une couleur transparente. Mais bon, j’ai dus me tromper quelque part. Comment faut-il pour dessiner le triangle avec un facteur de transparence ?

  4. En gros il faut activer la transparence, spécifier un facteur de transparence pour la couleur associée aux vertices de ta forme et afficher le tout. A ce stade de tes connaissances il est difficile d’expliquer rapidement comment faire. Je peux toutefois te donner un avant goût. Il serait mieux que tu sois déjà au chapitre 8 sur les textures pour mieux comprendre ce que je vais faire ici.

    Prend le troisième projet du chapitre 8 (sur les textures). Remplace la classe Game1 par ça :

        public class Game1 : Microsoft.Xna.Framework.Game< ?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

        {

            private GraphicsDeviceManager graphics;

            private ContentManager content;

            private Vector3 position = Vector3.Zero;

            private Vector3 size = Vector3.One;

            private Cube _cube;

     

            private Cube _cube2;

     

     

            public Game1()

            {

                graphics = new GraphicsDeviceManager(this);

                content = new ContentManager(Services);

     

                this._cube = new Cube();

                this._cube2 = new Cube();

            }

     

     

            /// <summary>

            /// Allows the game to perform any initialization it needs to before starting to run.

            /// This is where it can query for any required services and load any non-graphic

            /// related content.  Calling base.Initialize will enumerate through any components

            /// and initialize them as well.

            /// </summary>

            protected override void Initialize()

            {

                // TODO: Add your initialization logic here

                this.InitializeRenderTarget();

                this.InitializeCubes();

                this.InitializeEffect();

                this.SetupDevice();

     

                base.Initialize();

            }

     

            private void InitializeCubes()

            {

                this._cube.IsObjectOriginInCubeCenter = true;

                this._cube.Color = new Color(255, 255, 255, 120);

                this._cube.Load(this.graphics.GraphicsDevice);

     

                this._cube2.IsObjectOriginInCubeCenter = true;

                this._cube2.Color = new Color(255, 255, 255, 120);

                this._cube2.Load(this.graphics.GraphicsDevice);

            }

     

            private void SetupDevice()

            {

                this.graphics.GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;

            }

     

            private void InitializeEffect()

            {

                Matrix viewMatrix = Matrix.CreateLookAt(new Vector3(< ?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />0f, -4f, 2f), new Vector3(0f, 0f, 0f), new Vector3(0, 0, 1f));

                Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.Window.ClientBounds.Width / this.Window.ClientBounds.Height, 1.0f, 100.0f);

     

                this._cube.Effect.View = viewMatrix;

                this._cube.Effect.Projection = projectionMatrix;

     

                this._cube2.Effect.View = viewMatrix;

                this._cube2.Effect.Projection = projectionMatrix;

            }

     

            private void InitializeRenderTarget()

            {

                this.graphics.IsFullScreen = false;

                this.graphics.PreferredBackBufferWidth = 800;

                this.graphics.PreferredBackBufferHeight = 600;

                this.graphics.ApplyChanges();

     

                this.Window.Title = “Huitieme Tutorial : Cube texturé !”;

                this.Window.AllowUserResizing = true;

            }

     

     

     

            /// <summary>

            /// Load your graphics content.  If loadAllContent is true, you should

            /// load content from both ResourceManagementMode pools.  Otherwise, just

            /// load ResourceManagementMode.Manual content.

            /// </summary>

            /// <param name=”loadAllContent”>Which type of content to load.</param>

            protected override void LoadGraphicsContent(bool loadAllContent)

            {

                if (loadAllContent)

                {

                   

                    // TODO: Load any ResourceManagementMode.Automatic content

                    Texture2D texture = content.Load<Texture2D>(“autretexture”);

                    this._cube.Texture = texture;

                    this._cube2.Texture = texture;

                }

     

                // TODO: Load any ResourceManagementMode.Manual content

                this.graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;

                this.graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceColor;

                this.graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.DestinationColor;

                this.graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;

            }

     

     

            /// <summary>

            /// Unload your graphics content.  If unloadAllContent is true, you should

            /// unload content from both ResourceManagementMode pools.  Otherwise, just

            /// unload ResourceManagementMode.Manual content.  Manual content will get

            /// Disposed by the GraphicsDevice during a Reset.

            /// </summary>

            /// <param name=”unloadAllContent”>Which type of content to unload.</param>

            protected override void UnloadGraphicsContent(bool unloadAllContent)

            {

                if (unloadAllContent == true)

                {

                    content.Unload();

                }

            }

     

     

            /// <summary>

            /// Allows the game to run logic such as updating the world,

            /// checking for collisions, gathering input and playing audio.

            /// </summary>

            /// <param name=”gameTime”>Provides a snapshot of timing values.</param>

            protected override void Update(GameTime gameTime)

            {

                // Allows the default game to exit on Xbox 360 and Windows

                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)

                    this.Exit();

     

                // TODO: Add your update logic here

                //modulo

                long iTime = (long)(gameTime.TotalGameTime.TotalMilliseconds % 2000f);

                //passage en radian

                float fAngle = iTime * (2.0f * MathHelper.Pi) / 2000.0f;

     

                position += new Vector3(0, 0, 0.05f * (float)Math.Cos(fAngle));

     

                this._cube2.SetSize(size);

                this._cube2.SetPosition(position + new Vector3(0, 5f, 0));

                this._cube2.SetRotation(0, 0, -fAngle);

     

                this._cube.SetSize(size);

      

                base.Update(gameTime);

            }

     

     

            /// <summary>

            /// This is called when the game should draw itself.

            /// </summary>

            /// <param name=”gameTime”>Provides a snapshot of timing values.</param>

            protected override void Draw(GameTime gameTime)

            {

                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

     

                // TODO: Add your drawing code here

     

                this._cube2.Render();

                this._cube.Render();

     

                base.Draw(gameTime);

            }

        }

     

    En gros j’affiche ici deux cubes. L’un au premier plan immobile, l’autre derrière qui se déplace de bas en haut. Le premier cube est transparent. La transparence ici ne se fait pas au niveau de la couleur mais lors du “dessin” de l’objet à l’écran. Deux instructions permettent cela :

    this.graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceColor;

    this.graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.DestinationColor;

          

    Blend veut dire “melanger”. En gros je dis ici, “lors du dessin d’une forme 3D, mélange la couleur de chaque pixel de cette forme (source) avec la couleur de chaque pixel déjà présent (destination). Je reviendrais plus en détail sur le fonctionnement d’un blend et expliquerai chacun des membres de l’énumération Blend.

     Pour activer et desactiver le blending on procède ainsi : 

    this.graphics.GraphicsDevice.RenderState.AlphaBlendEnable = false;

    this._cube2.Render();

    this.graphics.GraphicsDevice.RenderState.AlphaBlendEnable = true;

    this._cube.Render();

     Le second cube (non transparent) est affiché d’abord et le blending est desactivé, le premier cube est affiché ensuite avec le blend activé.

    A l’affichage du obtiens :

     

    Si tu veux rendre l’intérieur du cube visible (paroies transparentes) utilise le Back Face Culling.

    Le blending correspond à l’opération suivante :

    couleur finale = couleur source * blendsource + couleur destination * blenddestination 

    chaque opérande ici est composé de quatre valeur bien connues : RGBA (rouge, vert, bleu, alpha).C’est blendsource et blenddestination que nous specifions dans les deux instructions précédentes. 

    Le premier cube semble bleuté car la couleur de chacun de ses pixels à l’affichage s’additionne à ceux du fond qui est bleu. Si tu met une couleur blanche en fond, le cube transparent va disparaitre, une couleur noir donne un affichage des couleurs du premier cube parfait. Lorsque les deux cubes se supperposent leurs couleurs “s’additionnent” donnant un effet de transparence. Il faut faire attention à l’ordre dans lequel tu affiche les cubes. Si tu inverse l’affichage du cube 2 avec le cube 1 tu n’auras plus de transparence car le cube transparent est affiché dans un espace ou le cube normal n’est pas encore présent.

    Tout cela fera l’objet d’un chapitre.

     

     

     

     

     

     

     

     

  5. je n’arrive pas a faire le copier coller et je n’arrive pas a faire la premiere et la deuxième etapes de la modif de source

  6. Merci pour cet exemple. Je vais regarder cela demain et voir si j’arrive a mes fins, c’est-a-dire afficher un rectangle en 2D avec une couleur transparente.

  7. Bon, je suis arrivé a afficher mon rectangle en transparence. Cependant il y a une erreur dans l’exemple affichant un cube en transparence.

    Il est dis que la transparence s’active avec les instructions suivantes:

    this.graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceColor;

    this.graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.DestinationColor;  

    Cela produit bien un effet de mélange des couleurs entre l’objet affiché et le fond de l’image, mais sans tenir compte de l’indice de transparence. Pour y parvenir j’ai dus remplacer le paramétre Blend.SourceColor par Blend.SourceAlpha.

    La formule que j’ai utilisé est:

    this.graphics.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;

    this.graphics.GraphicsDevice.RenderState.DestinationBlend = Blend.DestinationColor;  

    Il semble y avoir des tas de manières différentes de faire une opération de blending.

  8. Oui ce n’est pas une erreur, c’est une facon de réaliser une transparence entre des surface n’ayant pas d’informations de type Alpha.
    Comme tu le dis il y’a des tas de manières. Toutes ont une utilité dans un cadre bien précis.

  9. Si je veux tracer une ligne entre deux points dans l’espace, doit-je utiliser un triangle “plat”, en donnant les mêmes coordonnées pour le second et le troisième vertex ? Ou existe-il une méthode plus efficace ?

    // tracé ligne (x1,y1,z1)-(x2,y2,z2)
    Vertex_1 = (x1,y1,z1)
    Vertex_2 = (x2,y2,z2)
    vertex_3 = (x2,y2,z2)
    tracé du triangle (vertex_01, Vertex_02, Vertex_03)

  10. oui il y’a une méthode plus simple, utilise le tracé de primitive LineList.
    Dans le tbleau de vertices tu passe alors deux vecteurs : x1 et x2

  11. Salut, ca va peut être sembler bête mais j’arrive pas à mettre la main sur un fichier : effects.fx
    Même en recherchant sur le net…
    Pouvez vous m’aider ?

  12. J’ai un petit probleme… Le dossier debug (SecondProjet\bin\x86\debug) est vide :) Pas de trace de effect.fx. J’ai lancé une recherche avec l’outil de Windows Vista (qui, contre toutes les rumeurs, est exelents – je parle de Vista) et il n’a rien trouvé…

    Can you help me ? :P

  13. Salut,

    J’ai 13 ans et je viens de commencer à programmer. Gâce a ton site j’ai apris beaucoup de chose et je vais encore en apprendre mais pour le moment après avoir fait ce tutorial qui devait donner sa:
    [img]http://msmvps.com/photos/valentin/images/489746/619×480.aspx[/img]
    Mais ensuite je me suis amuser a crée un maison et j’ai bien réussi sa donner sa.
    [img]www.ordinasoft.ch/public_html/Romain/Forum/image/Sans-titre-3.jpg[/img]
    Je suis très fière de moi et je te remércie encore pour ses beaux tutoriaux.

  14. merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
    merciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii

    j’etais vraiment nul dans hlsl le fichier effect est m’as toujours me fais peur, maintenant c’est passe, c’est moi qui va lui faire peur et c’est grace a toi, je veux t’embrasse sur ta front et dans ta main parce que tu as m’eclaire ce que j’avais jamais connu et que j’etais au point de laisse tombe HLSL.

    je veux que m’explique quelque chose s’il vous plait :

    au depart dans la classe float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0

    on voit bien que le type de return est rouge, car il a R=1,V=0,B=0 et alpha=0

    c’est simple.

    return float4(1, 0, 0, 1);

    mais qu’on tu as applique :

    return input.Color;

    le triagle est trouve en couleur ??? et c’est bien ce que j’ai pas compris.

    sous OpenGl on dois donne une coleur pour chaque vertex, exemple :

    glBegin( GL_TRIANGLES );
    glColor3f(1,0,0);
    glVertex3f( 0.0f, 1.0f, e );
    glColor3f(0,1,0);
    glVertex3f( -1.0f, -1.0f, e );
    glColor3f(0,0,1);
    glVertex3f( 1.0f, -1.0f, e );
    glEnd( );

    mais sous le fichier effect !!! je vois pas ?

    on n’est declare float4 Color : COLOR0;

    et les conteurs sont bien colories!!! comment il sait que je veux couleur les conteurs de mon triangle ?, et si je veux modifier la coleur de chaque conteur ?

    par exemple :
    dans ta photo final la couleur rouge est en haut comment je vais faire si je veux mettre la couleur bleu a sa place ?

  15. Salut,

    ahmed je pense que je peut repondre a ta question. Regarde les quelques lignes de code de la methode Initialize():
    vertices = new VertexPositionColor[3];
    vertices[2].Position = new Vector3(-0.5f, -0.5f, 0f);
    vertices[2].Color = Color.Red; // = rouge
    vertices[1].Position = new Vector3(0, 0.5f, 0f);
    vertices[1].Color = Color.Green; // = vert
    vertices[0].Position = new Vector3(0.5f, -0.5f, 0f);
    vertices[0].Color = Color.Yellow; // = jaune

    sinon Valentin merci pour ce tuto tres bien fait !

    bonne continuation

    Nicolas

  16. Bonjour,

    Je me lance dans la réalisation de jeu en 3D et je dois dire que plus j'avance dans la rédaction de tes tutoriels plus je suis stupéfait par la manière dont tu expliques si bien tout cela. Car il est vrai que lorsque l'on ne dispose de "rien" et que l'on veux commencer a créer un jeu à partir de C# et XNA, c'est vraiment la pagaille!! On y comprend rien et on est très vite perdu puis découragé !!

    En tout cas bravo pour ces tutoriels qui sont très compréhensible et bien expliqués pas à pas! J'ai presque envie de dire que n'importe qui (oui oui) avec un minimum de connaissance en c# ainsi qu'en POO, un peu de réflexion ainsi que du temps pour lire et comprendre tes tutoriels peut très bien réussir à creer quelque chose!

    Bravo !

  17. Bonjour,

    Je me lance dans la réalisation de jeu en 3D et je dois dire que plus j’avance dans la rédaction de tes tutoriels plus je suis stupéfait par la manière dont tu expliques si bien tout cela. Car il est vrai que lorsque l’on ne dispose de “rien” et que l’on veux commencer a créer un jeu à partir de C# et XNA, c’est vraiment la pagaille!! On y comprend rien et on est très vite perdu puis découragé !!

    En tout cas bravo pour ces tutoriels qui sont très compréhensible et bien expliqués pas à pas! J’ai presque envie de dire que n’importe qui (oui oui) avec un minimum de connaissance en c# ainsi qu’en POO, un peu de réflexion ainsi que du temps pour lire et comprendre tes tutoriels peut très bien réussir à creer quelque chose!

    Bravo !

  18. jarrive po à comprendre comment vous faites pour choisir ces valeurs
    new Vector3(-0.5f, -0.5f, 0f); ???? svp mercii d’avance

  19. simple : (1,0,0) correspond au coin bas droite, (0,0,0) au coin bas gauche et (0,1,0) au cas haut gauche et enfin (1,1,0) au cas haut droite :)

  20. Salut,

    Déjà merci énormément pour ce tutoriel de qualité.

    Ma question est un peu saugrenue, j’avoue !

    Je bosse (Mwi, bon, c’est un loisir, c’est mal de faire croire que je travaille ! avec C# 2008 et le 3.1 du XNA. Je fais exactement comme tu le dis pour ajouter le fichier fx sauf que moi, il n’a pas de template.

    Persistante, j’ai créer un fichier fx à la main, j’y ai recopié tout ce que tu as mis et j’ai voulu rajouter la ligne là :

    protected override void LoadContent()
    {
    // Create a new SpriteBatch, which can be used to draw textures.
    spriteBatch = new SpriteBatch(GraphicsDevice);
    this.effect = Content.Load(“Effect”);

    // TODO: use this.Content to load your game content here
    }

    Et pan erreur dans la vue : lovesunset.game1 ne contient pas de définition pour effect.

    Est-ce que j’ai raté une étape?

    (LoveSunset, c’est le nom de l’appli, je trouvais ça plus joli que second projet…)

  21. Je me reprends (si vous pouvez, éditez le précédent commentaire pour y ajouter ceci : )

    J’avais oublié la déclaration suivante :
    Effect effect;

    Tout bêtement… mais important, surtout en programmation.

  22. Bonjour.
    Je m’essaye au XNA 4.0.
    Le VertexDeclaration de this.graphics.GraphicsDevice.VertexDeclaration n’existe plus dans le XNA 4.0.
    Ainsi:
    this.graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(this.graphics.GraphicsDevice, VertexPositionColor.VertexElements);
    this.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, 1);
    ne fonctionne plus

    J’ai essayé d’agir ainsi:
    VertexBuffer vb= new VertexBuffer(this.graphics.GraphicsDevice,typeof(VertexPositionColor),vertices.Length,BufferUsage.None);
    this.graphics.GraphicsDevice.SetVertexBuffer(vb); this.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, 1);

    Ça compile mais je n’obtiens rien à l’écran. ( J’ai bien rajouté l’Effect)
    Quelqu’un saurait comment agir en 4.0?

  23. Voila en farfouillant un peu j’ai trouvé comment afficher le triangle avec XNA 4.0:

    Tout d’abord déclarer un BasicEffect (Effect.fx devient donc useless):

    BasicEffect basicEffect;

    Puis dans Initialize:

    protected override void Initialize()
    {
    // TODO: Add your initialization logic here
    basicEffect = new BasicEffect(graphics.GraphicsDevice);
    basicEffect.World = Matrix.Identity;
    basicEffect.VertexColorEnabled = true;
    basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, this.GraphicsDevice.Viewport.AspectRatio, 0.1f, 100f);
    basicEffect.View = Matrix.CreateLookAt(new Vector3(0, 0, -2), Vector3.Zero, Vector3.Up);
    vertices = new VertexPositionColor[3];
    vertices[2].Position = new Vector3(-0.5f, -0.5f, 0f);
    vertices[2].Color = Color.Red;
    vertices[1].Position = new Vector3(0, 0.5f, 0f);
    vertices[1].Color = Color.Green;
    vertices[0].Position = new Vector3(0.5f, -0.5f, 0f);
    vertices[0].Color = Color.Yellow;
    base.Initialize();
    }

    Puis enfin dans Draw:

    protected override void Draw(GameTime gameTime)
    {
    GraphicsDevice.Clear(Color.CornflowerBlue);
    basicEffect.CurrentTechnique.Passes[0].Apply();
    this.graphics.GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vertices, 0, 1);
    // TODO: Add your drawing code here
    base.Draw(gameTime);
    }

    Voila en espérant que ça puisse aider :)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>