10 Matériaux : Concepts de base

Je sais que la dernière fois j'ai dit que j'enchaînerai sur les meshes, mais j'ai menti.

En fait, j'ai fait une bêtise. Ce qu'il s'est passé et que j'ai commencé a écrire un article sur les meshes quand j'ai réalisé qu'utiliser les textures et les meshes en même temps, c'était un peu trop pour une introduction. Donc j'ai décidé que nous devrions parler des matériaux, ce qui nous donnera un bonne base pour aborder les meshes. Ensuite nous verrons les Z-Buffers aussi, bien sûr.

Nous avons maintenant vu 2 façons de coloriser des objets dans notre scène : on peut utiliser soit des formats de sommets qui possèdent de l'information de couleurs, ou nous pouvons utiliser une texture pour envelopper un bitmap sur un triangle ou un ensemble de triangles. Mais il s'avère qu'il existe encore un autre moyen : l'utilisation de matériaux.

Un matériau est un ensemble de paramètres globaux qui disent au Device "Hey, la prochaine fois que tu dessines un triangle, je voudrais qu'il ressemble à ça". Les paramètres sont encapsulés dans une structure de type Material, et sont associés au Device en affectant la propriété du même nom. Ca se passe comme ceci :

protected void SetupMaterials() { Material mat = new Material(); // Configurer les propriétés du matériau // (Code omis pour le moment) device.Material = mat; }

Quoi de plus simple?

La structure Material n'a que quelques propriétés, détaillons les l'une après l'autre.

Material.Diffuse : configure la couleur de diffusion du matériau. Vous vous rappellerez de la définition de diffuse de l'article sur les lumières. Pour résumer, c'est la couleur "normale" d'un objet.

Material.Ambient : configure la couleur ambiante du matériau. C'est la couleur de l'objet qui intéragit avec la lumière ambiante. Nous avons également vu cela dans l'introduction aux lumières. La couleur ambiante sera habituellement la même que la couleur de diffusion.

Material.Emissive : définit la couleur émissive (ndt : pas de traduction trouvée pour emissive) du matériau. La couleur émissive est la composante de la couleur d'un objet qui ne change pas en fonction du taux de lumière l'illuminant. Contrairement aux couleurs ambiantes et de diffusion qui dépendent du taux de lumière de diffusion et d'ambience parvenant à l'objet, la couleur émissive sera présente même si l'objet est dans l'obscurité totale. Ca a tendance à donner l'impression que l'objet rayonne, bien que ça ne produise aucune lumière. Pour cette raison, vous remarquerez que la couleur émissive n'est pas utilisée très souvent. C'est cependant parfois utile pour des effets spéciaux, comme afficher un soleil ou une ampoule bien que nous pourrions toujours mettre une source de lumière dans l'objet. Nous la mettrons en noir dans nos exemples.

Material.Specular : configure la couleur spéculaire d'un matériau. Que vous la sachiez ou non, vous connaissez déjà la spécularité. La spécularité est un effet causé par des imperfections microscopiques à la surface d'un objet. Cela se manifeste comme une tâche lumineuse à la surface d'un objet. Pensez à une image de pomme ; c'est rouge, mais il y a une petite partie blanche où la lumière la touche. Cela s'appelle un reflet spéculaire. Si vous spécifiez la couleur spéculaire en blanc, vous allez créer un reflet spéculaire (à condition d'avoir un éclairage blanc), ce qui nous donnera l'impression que l'objet est en plastique. Mettre une couleur spéculaire proche de la couleur de diffusion nous donnera l'impression que l'objet est en métal. Voici 2 images démontrant la différence (ndt : il semble n'y en avoir qu'une en fait) :

La première image semble être en plastique, la seconde (ndt : ?) plus métallique, bien que l'effet est subtil, particulièrement sans réflexion (un sujet pour une autre fois).

Material.SpecularSharpness : contrôle comment le reflet spéculaire est serré. Un petit nombre donnera un reflet plus étalé, rendant un objet plus terne. Un plus grand nombre donnera un reflet plus serré, rendant l'objet plus brillant. Voici un objet avec 2 différentes valeurs (ndt : là aussi, il semble n'y avoir qu'une image) :

La première image a une valeur de 50. La deuxième a une valeur de 10. Vous pouvez constater (ndt : mouais) comment le reflet est plus étalé. Nous voudrions peut être utiliser une couleur spéculaire un peu plus sombre pour augmenter l'attenuation de l'objet.

Le reflet spéculaire est généré en fonction des mêmes vecteurs normaux que le modèle d'éclairage, et a les mêmes limites : un objet plus lisse semblera plus réaliste car le reflet sera plus lisse, mais cela nécessitera plus de triangles et sera ainsi plus lent à calculer.

Etant donné que nous comprenons à présent la fonction des divers champs de la structure Material, nous pouvons finir de remplir notre fonction SetupMaterials.

protected void SetupMaterials() { Material mat = new Material(); // Configure les propriétés du matériau // L'objet lui meme sera bleu mat.Diffuse = Color.Blue; // Nous voulons qu'il ait l'air un peu terne, donc peut être un reflet large et gris. mat.Specular = Color.LightGray; mat.SpecularSharpness = 15.0F; device.Material = mat; // Très important, sans cela, il n'y a pas de spécularité device.RenderState.SpecularEnable = true; }

Notez l'instruction de la plus haute importance qui met SpecularEnable à vrai.

Après avoir activé la spécularité, chaque triangle affiché sera bleu et quelque peu brillant. Si nous voulons afficher des triangles bleus et d'autres jaunes, nous devront affecter à Device.Material au matérieu bleu, afficher les triangles bleus, ensuite mettre un matériau jaune dans Device.Material et afficher les triangles jaunes.. Material est un paramètre global !

Le code complet apparaît ci-dessous. Nous sommes familiers à la plupart de ce dernier maintenant. J'utilise un cylindre plutot qu'un simple triangle car les reflets spéculaires sont plus visibles avec des objets courbés. J'ai également rajouté l'appel à SetupMaterials dans Render. Techniquement, j'aurais pu le mettre dans InitializeGraphics étant donné que le matériau ne change jamais, mais vous avez l'idée générale.

La prochaine fois, nous aborderons une question que j'aimerais traiter avant d'attaquer les meshes : les z-buffers.

Le Code

Update

Si vous utilisez le SDK d'Octobre 2004 (ou supérieur), vous aurez besoin de remplacer les méthodesSetPosition et SetNormal ci dessous avec des affectations aux propriétés Position etNormal. Aussi, vous devrez remplacer l'appel à Commit dans SetupLights par un appel àUpdate. Ceci est du aux chagements que l'équipe DirectX ont fait au SDK d'Octobre 2004.

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.DirectX.Direct3D { public class Game : System.Windows.Forms.Form { private Device device; private VertexBuffer vertices; static void Main() { Game app = new Game(); app.Width = 800; app.Height = 600; 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; pres.EnableAutoDepthStencil = true; pres.AutoDepthStencilFormat = DepthFormat.D16; device = new Device( 0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, pres ); device.RenderState.CullMode = Cull.None; vertices = CreateVertexBuffer(device); return true; } protected void PopulateVertexBuffer(VertexBuffer vertices) { CustomVertex.PositionNormal[] verts = (CustomVertex.PositionNormal[]) vertices.Lock(0, 0); // Crée un cylindre fait d'un paquets de triangles for (int i = 0; i < 50; i++) { float theta = (float)(2 * Math.PI * i) / 49; verts[2 * i].SetPosition( new Vector3( (float)Math.Sin(theta), -1, (float)Math.Cos(theta) ) ); verts[2 * i].SetNormal( new Vector3( (float)Math.Sin(theta), 0, (float)Math.Cos(theta) ) ); verts[2 * i + 1].SetPosition( new Vector3( (float)Math.Sin(theta), 1, (float)Math.Cos(theta) ) ); verts[2 * i + 1].SetNormal( new Vector3( (float)Math.Sin(theta), 0, (float)Math.Cos(theta) ) ); } vertices.Unlock(); } protected VertexBuffer CreateVertexBuffer(Device device) { VertexBuffer buf = new VertexBuffer(typeof(CustomVertex.PositionNormal), 100, device, 0, CustomVertex.PositionNormal.Format, Pool.Default); PopulateVertexBuffer(buf); return buf; } protected void SetupMatrices() { float yaw = Environment.TickCount / 1000.0F; float pitch = Environment.TickCount / 612.0F; device.Transform.World = Matrix.RotationYawPitchRoll(yaw, pitch, 0); device.Transform.View = Matrix.LookAtLH(new Vector3(0, 0, -6), new Vector3(0, 0, 0), new Vector3(0, 1, 0)); device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI/4.0F, 1.0F, 1.0F, 10.0F); } protected void SetupLights() { device.RenderState.Lighting = true; device.Lights[0].Diffuse = Color.White; device.Lights[0].Specular = Color.White; device.Lights[0].Type = LightType.Directional; device.Lights[0].Direction = new Vector3(-1, -1, 3); device.Lights[0].Commit(); device.Lights[0].Enabled = true; device.RenderState.Ambient = Color.FromArgb(0x40, 0x40, 0x40); } protected void SetupMaterials() { Material mat = new Material(); // Configure les propriétés du matériau // L'objet lui meme sera bleu mat.Diffuse = Color.Blue; // Nous voulons qu'il ait l'air un peu terne, donc peut être un reflet large et gris. mat.Specular = Color.LightGray; mat.SpecularSharpness = 15.0F; device.Material = mat; // Très important, sans cela, il n'y a pas de spécularité device.RenderState.SpecularEnable = true; } protected void Render() { device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Bisque, 1.0F, 0); device.BeginScene(); SetupMatrices(); SetupLights(); SetupMaterials(); device.VertexFormat = CustomVertex.PositionNormal.Format; device.SetStreamSource(0, vertices, 0); device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 98); device.EndScene(); device.Present(); } protected void DisposeGraphics() { vertices.Dispose(); device.Dispose(); } } }