DirectX 11 - Tutoriel 7 : Rendu de modèles 3D

RasterTek

Traducteur

Jérôme Marsaguet

Ce tutoriel portera sur le rendu de modèles 3D dans DirectX 11 en utilisant HLSL. Le code de ce tutoriel est basé sur celui du tutoriel sur l'éclairage diffus.

Commentez

- Titre : DirectX 11 - Tutoriel 7 : Rendu de modèles 3D

- Auteur : RasterTek

- Traducteur : Jérôme Marsaguet

- Parution : 6 août 2014

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2014 RasterTek. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Navigation

- I. Introduction

- II. Cube.txt

- III. Modelclass.h

- IV. Modelclass.cpp

- V. Graphicsclass.h

- VI. Graphicsclass.cpp

- VII. Résumé

- VIII. Exercices à faire

- IX. Code source

- X. Remerciements

- Navigation

Navigation

Tutoriel précédent : lumière diffuse

Sommaire

Tutoriel suivant : chargement de modèles Maya 2011

I - Introduction

Nous avons déjà effectué le rendu de modèles 3D dans les tutoriels précédents, mais ils n'étaient composés que d'un seul triangle, ce qui n'était pas très intéressant. Maintenant que nous avons vu les bases, nous allons aller de l'avant pour effectuer le rendu d'un objet plus complexe : un cube. Avant d'aborder la façon de rendre les modèles plus complexes, nous allons parler des formats de modèles.

Il existe de nombreux outils permettant aux utilisateurs de créer des modèles 3D. Maya et 3D Studio Max sont les deux programmes de modélisation 3D les plus populaires. Il y a beaucoup d'autres outils avec moins de fonctionnalités, mais qui gèrent très bien les bases dont nous avons besoin.

Peu importe l'outil que vous choisissez d'utiliser, ils exporteront tous leurs modèles dans de nombreux formats différents. Je vous suggère de créer votre propre format et d'écrire un analyseur capable de convertir leur format d'export vers le vôtre. La raison en est que le logiciel de modélisation 3D que vous utilisez peut changer avec le temps, de même que son format de modèle. Aussi, utiliserez-vous peut-être plusieurs logiciels de modélisation 3D et aurez donc différents formats à traiter. Donc, si vous convertissez tous ces formats vers votre unique format, votre code n'aura jamais besoin de changer. Vous aurez juste à modifier vos convertisseurs permettant de traduire ces formats vers le vôtre. La plupart des logiciels de modélisation 3D exportent aussi une tonne de déchets, nécessaires au logiciel, mais dont nous n'avons aucune utilité, et que nous ne reportons donc pas dans notre format de modèle.
La partie la plus importante à retenir pour faire votre propre format est qu'il doit couvrir tout ce que vous avez besoin de faire et qu'il doit rester simple d'utilisation. Vous pouvez également envisager de faire différents formats pour des objets différents, certains pouvant contenir des données d'animation, d'autres pouvant être statiques, et ainsi de suite.

Le format de modèle que je vais vous présenter est très basique. Il contiendra une ligne pour chaque sommet du modèle. Chaque ligne correspondant au format de sommet utilisé dans le code qui sera vecteur de position (x, y, z), les coordonnées de texture (tu, tv) et la normale (nx, ny, nz). Le format contiendra également le nombre de sommets au début, de sorte à pouvoir lire la première ligne et allouer la mémoire nécessaire pour les structures, avant d'effectuer la lecture des données. Le format exige que chaque ligne représente un triangle et que les sommets soient présentés dans le sens horaire. Voici le fichier du cube dont nous allons effectuer le rendu :

II - Cube.txt

Vertex Count: 36

Data:

-1.0  1.0 -1.0 0.0 0.0  0.0  0.0 -1.0
1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0
-1.0 -1.0 -1.0 0.0 1.0  0.0  0.0 -1.0
1.0  1.0 -1.0 1.0 0.0  0.0  0.0 -1.0
1.0 -1.0 -1.0 1.0 1.0  0.0  0.0 -1.0
1.0  1.0 -1.0 0.0 0.0  1.0  0.0  0.0
1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0
1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0
1.0 -1.0 -1.0 0.0 1.0  1.0  0.0  0.0
1.0  1.0  1.0 1.0 0.0  1.0  0.0  0.0
1.0 -1.0  1.0 1.0 1.0  1.0  0.0  0.0
1.0  1.0  1.0 0.0 0.0  0.0  0.0  1.0
-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0
1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0
1.0 -1.0  1.0 0.0 1.0  0.0  0.0  1.0
-1.0  1.0  1.0 1.0 0.0  0.0  0.0  1.0
-1.0 -1.0  1.0 1.0 1.0  0.0  0.0  1.0
-1.0  1.0  1.0 0.0 0.0 -1.0  0.0  0.0
-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0
-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0
-1.0 -1.0  1.0 0.0 1.0 -1.0  0.0  0.0
-1.0  1.0 -1.0 1.0 0.0 -1.0  0.0  0.0
-1.0 -1.0 -1.0 1.0 1.0 -1.0  0.0  0.0
-1.0  1.0  1.0 0.0 0.0  0.0  1.0  0.0
1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0
-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0
-1.0  1.0 -1.0 0.0 1.0  0.0  1.0  0.0
1.0  1.0  1.0 1.0 0.0  0.0  1.0  0.0
1.0  1.0 -1.0 1.0 1.0  0.0  1.0  0.0
-1.0 -1.0 -1.0 0.0 0.0  0.0 -1.0  0.0
1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0
-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0
-1.0 -1.0  1.0 0.0 1.0  0.0 -1.0  0.0
1.0 -1.0 -1.0 1.0 0.0  0.0 -1.0  0.0
1.0 -1.0  1.0 1.0 1.0  0.0 -1.0  0.0

Comme vous pouvez le voir il y a 36 lignes de données x, y, z, tu, tv, nx, ny et nz. Chaque groupe de trois lignes compose un triangle, ce qui nous donne douze triangles formant un cube. Le format est très simple et peut être lu directement dans nos tampons de sommets et rendu sans aucune modification.

Faites attention au fait que certains programmes de modélisation 3D exportent leurs données dans des ordres différents, comme les systèmes de coordonnées directs et indirects. Rappelez-vous que par défaut, dans DirectX 11, le système de coordonnées est indirect et que les données du modèle doivent donc suivre. Restez attentif à ces différences et assurez-vous que votre programme d'analyse puisse gérer la conversion des données dans le bon format/ordre.

III - Modelclass.h

Pour ce tutoriel, la classe ModelClass ne nécessite que quelques modifications mineures, afin qu'elle effectue le rendu de modèles 3D à partir de nos fichiers texte de modèle.

////////////////////////////////////////////////////////////////////////////////
// Nom du fichier : modelclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _MODELCLASS_H_
#define _MODELCLASS_H_

////////////////
// INCLUSIONS //
////////////////
#include <d3d11.h>
#include <d3dx10math.h>

La bibliothèque fstream est maintenant incluse pour pouvoir effectuer la lecture de nos fichiers.

#include <fstream>
using namespace std;

/////////////////////////////
// INCLUSIONS DE MA CLASSE //
/////////////////////////////
#include "textureclass.h"

////////////////////////////////////////////////////////////////////////////////
// Nom de la classe : ModelClass
////////////////////////////////////////////////////////////////////////////////
class ModelClass
{
private:
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR2 texture;
D3DXVECTOR3 normal;
};

La modification suivante concerne l'ajout d'une nouvelle structure nommée ModelType pour représenter le format du modèle. Elle contient la position du sommet, les coordonnées de la texture et les normales : les mêmes que notre format de fichier.

struct ModelType // NOUVEAU
{
float x, y, z;
float tu, tv;
float nx, ny, nz;
};

public:
ModelClass();
ModelClass(const ModelClass&);
~ModelClass();

La méthode Initialize prend dorénavant en paramètre le nom du fichier du modèle à charger.

bool Initialize(ID3D11Device*, char*, WCHAR*); // MODIFIÉ
void Shutdown();
void Render(ID3D11DeviceContext*);

int GetIndexCount();
ID3D11ShaderResourceView* GetTexture();

private:
bool InitializeBuffers(ID3D11Device*);
void ShutdownBuffers();
void RenderBuffers(ID3D11DeviceContext*);

bool LoadTexture(ID3D11Device*, WCHAR*);
void ReleaseTexture();

Nous rajoutons deux méthodes permettant de charger les données du fichier et de les libérer.

bool LoadModel(char*); // NOUVEAU
void ReleaseModel();   // NOUVEAU

private:
ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
int m_vertexCount, m_indexCount;
TextureClass* m_Texture;

Nous ajoutons enfin un nouveau membre privé nommé m_model qui sera le pointeur vers le tableau de structures ModelType. Cette variable sera utilisée pour lire et stocker les données du modèle avant qu'elles soient placées dans le tampon de sommets.

ModelType* m_model; // NOUVEAU
};

#endif

IV - Modelclass.cpp

////////////////////////////////////////////////////////////////////////////////
// Nom du fichier : modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"

ModelClass::ModelClass()
{
m_vertexBuffer = 0;
m_indexBuffer = 0;
m_Texture = 0;

Le nouveau pointeur est initialisé à la valeur nulle dans le constructeur de la classe.

m_model = 0; // NOUVEAU
}

ModelClass::ModelClass(const ModelClass& other)
{
}

ModelClass::~ModelClass()
{
}

La méthode Initialize prend dorénavant le nom du fichier du modèle à charger.

bool ModelClass::Initialize(ID3D11Device* device, char* modelFilename, WCHAR* textureFilename) // MODIFIÉ
{
bool result;

Dans la méthode Initialize, nous appelons maintenant en premier la nouvelle méthode loadModel. Elle va charger les données du modèle à partir du nom du fichier passé en argument dans le nouveau tableau m_model. Une fois ce tableau modèle rempli, nous pouvons construire les tampons de sommets et d'indices. Puisque InitializeBuffers dépend maintenant de ce modèle de données, assurez-vous d'appeler les méthodes dans le bon ordre.

// NOUVEAU - charge les données du modèle.
result = LoadModel(modelFilename);
if(!result)
{
return false;
}

// Initialise les tampons de sommets et d'indices.
result = InitializeBuffers(device);
if(!result)
{
return false;
}

// Charge la texture pour ce modèle.
result = LoadTexture(device, textureFilename);
if(!result)
{
return false;
}

return true;
}

void ModelClass::Shutdown()
{
// Libère la texture du modèle.
ReleaseTexture();

// Libère les tampons de sommets et d'indices.
ShutdownBuffers();

Nous ajoutons un appel à ReleaseModel dans la méthode Shutdown afin de libérer le pointeur m_model une fois terminé.

// NOUVEAU - libère les données du modèle.
ReleaseModel();

return;
}

void ModelClass::Render(ID3D11DeviceContext* deviceContext)
{
// Place les tampons de sommets et d'indices sur le pipeline graphique pour les préparer à être dessinés.
RenderBuffers(deviceContext);

return;
}

int ModelClass::GetIndexCount()
{
return m_indexCount;
}

ID3D11ShaderResourceView* ModelClass::GetTexture()
{
return m_Texture->GetTexture();
}

bool ModelClass::InitializeBuffers(ID3D11Device* device)
{
VertexType* vertices;
unsigned long* indices;
D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
D3D11_SUBRESOURCE_DATA vertexData, indexData;
HRESULT result;
int i;

Notez que nous ne fixons plus manuellement le nombre de sommets et d'indices ici. Vous verrez plus tard que nous récupérons cette valeur dans la méthode ModelClass::loadModel.

// Crée le tableau de sommets.
vertices = new VertexType[m_vertexCount];
if(!vertices)
{
return false;
}

// Crée le tableau d'indices.
indices = new unsigned long[m_indexCount];
if(!indices)
{
return false;
}

Le chargement des sommets et des indices des tableaux a légèrement changé. Au lieu de définir manuellement les valeurs, nous copions en boucle tous les éléments du nouveau tableau pointé par m_model dans le tableau de sommets. Le tableau d'indices est facile à construire car chaque sommet a le même indice que sa position dans le tableau où il a été chargé.

// NOUVEAU - charge les tableaux de sommets et d'indices avec les données.
for(i=0; i<m_vertexCount; i++)
{
vertices[i].position = D3DXVECTOR3(m_model[i].x, m_model[i].y, m_model[i].z);
vertices[i].texture = D3DXVECTOR2(m_model[i].tu, m_model[i].tv);
vertices[i].normal = D3DXVECTOR3(m_model[i].nx, m_model[i].ny, m_model[i].nz);

indices[i] = i;
}

// Met en place la description du tampon de sommets.
vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = 0;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;

// Donne à la structure de sous-ressources un pointeur vers le tableau des sommets.
vertexData.pSysMem = vertices;
vertexData.SysMemPitch = 0;
vertexData.SysMemSlicePitch = 0;

// Crée le tampon des sommets.
result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
if(FAILED(result))
{
return false;
}

// Met en place la description du tampon statique des indices.
indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
indexBufferDesc.CPUAccessFlags = 0;
indexBufferDesc.MiscFlags = 0;
indexBufferDesc.StructureByteStride = 0;

// Donne à la structure de sous-ressources un pointeur vers le tableau des indices.
indexData.pSysMem = indices;
indexData.SysMemPitch = 0;
indexData.SysMemSlicePitch = 0;

// Crée le tampon des indices.
result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
if(FAILED(result))
{
return false;
}

// Libère les tableaux, maintenant que les tampons ont été créés et remplis.
delete [] vertices;
vertices = 0;

delete [] indices;
indices = 0;

return true;
}

void ModelClass::ShutdownBuffers()
{
// Libère le tampon d'indices.
if(m_indexBuffer)
{
m_indexBuffer->Release();
m_indexBuffer = 0;
}

// Libère le tampon de sommets.
if(m_vertexBuffer)
{
m_vertexBuffer->Release();
m_vertexBuffer = 0;
}

return;
}

void ModelClass::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
unsigned int stride;
unsigned int offset;

// Règle le pas et l'offset du tampon.
stride = sizeof(VertexType);
offset = 0;

// Active le tampon des sommets dans l'assembleur d'entrées pour effectuer son rendu.
deviceContext->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

// Active le tampon des indices dans l'assembleur d'entrées pour effectuer son rendu.
deviceContext->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

// Définit le type de primitives utilisé. Ces primitives, ici des triangles, seront affichées depuis le tampon de sommets.
deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

return;
}

bool ModelClass::LoadTexture(ID3D11Device* device, WCHAR* filename)
{
bool result;

// Crée l'objet texture.
m_Texture = new TextureClass;
if(!m_Texture)
{
return false;
}

// Initialise l'objet texture.
result = m_Texture->Initialize(device, filename);
if(!result)
{
return false;
}

return true;
}

void ModelClass::ReleaseTexture()
{
// Libère l'objet texture.
if(m_Texture)
{
m_Texture->Shutdown();
delete m_Texture;
m_Texture = 0;
}

return;
}

Voici la nouvelle méthode loadModel qui gère le chargement des données du modèle dans le pointeur m_model à partir du fichier texte. Elle ouvre le fichier et commence par lire le nombre de sommets. Elle alloue alors le tableau de ModelType puis lit chaque ligne pour la mettre dans ce tableau. Le nombre de sommets et d'indices est dorénavant défini dans cette méthode.

bool ModelClass::LoadModel(char* filename)
{
ifstream fin;
char input;
int i;

// Ouvre le fichier du modèle.
fin.open(filename);

// Si le fichier n'a pas pu être ouvert, false est retourné.
if(fin.fail())
{
return false;
}

// La chaîne « Vertex Count: » est passée.
fin.get(input);
while(input != ':')
{
fin.get(input);
}

// Lecture du nombre de sommets.
fin >> m_vertexCount;

// Le nombre d'indices et le nombre de sommets sont les mêmes.
m_indexCount = m_vertexCount;

// Création du modèle en utilisant la valeur lue.
m_model = new ModelType[m_vertexCount];
if(!m_model)
{
return false;
}

// Placement au début des données à lire.
fin.get(input);
while(input != ':')
{
fin.get(input);
}
fin.get(input);
fin.get(input);

// Lecture des données.
for(i=0; i<m_vertexCount; i++)
{
fin >> m_model[i].x >> m_model[i].y >> m_model[i].z;
fin >> m_model[i].tu >> m_model[i].tv;
fin >> m_model[i].nx >> m_model[i].ny >> m_model[i].nz;
}

// Fermeture du fichier.
fin.close();

return true;
}

La méthode ReleaseModel se charge de libérer le tableau du modèle.

void ModelClass::ReleaseModel() // NOUVEAU
{
if(m_model)
{
delete [] m_model;
m_model = 0;
}

return;
}

V - Graphicsclass.h

L'en-tête de la classe GraphicsClass n'a subi aucun changement depuis le tutoriel précédent.

////////////////////////////////////////////////////////////////////////////////
// Nom du fichier : graphicsclass.h
////////////////////////////////////////////////////////////////////////////////
#ifndef _GRAPHICSCLASS_H_
#define _GRAPHICSCLASS_H_

/////////////////////////////
// INCLUSIONS DE MA CLASSE //
/////////////////////////////
#include "d3dclass.h"
#include "cameraclass.h"
#include "modelclass.h"
#include "lightshaderclass.h"
#include "lightclass.h"

////////////////////////
// VARIABLES GLOBALES //
////////////////////////
const bool FULL_SCREEN = true;
const bool VSYNC_ENABLED = true;
const float SCREEN_DEPTH = 1000.0f;
const float SCREEN_NEAR = 0.1f;

////////////////////////////////////////////////////////////////////////////////
// Nom de la classe : GraphicsClass
////////////////////////////////////////////////////////////////////////////////
class GraphicsClass
{
public:
GraphicsClass();
GraphicsClass(const GraphicsClass&);
~GraphicsClass();

bool Initialize(int, int, HWND);
void Shutdown();
bool Frame();

private:
bool Render(float);

private:
D3DClass* m_D3D;
CameraClass* m_Camera;
ModelClass* m_Model;
LightShaderClass* m_LightShader;
LightClass* m_Light;
};

#endif

VI - Graphicsclass.cpp

////////////////////////////////////////////////////////////////////////////////
// Nom du fichier : graphicsclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "graphicsclass.h"

GraphicsClass::GraphicsClass()
{
m_D3D = 0;
m_Camera = 0;
m_Model = 0;
m_LightShader = 0;
m_Light = 0;
}

GraphicsClass::GraphicsClass(const GraphicsClass& other)
{
}

GraphicsClass::~GraphicsClass()
{
}

bool GraphicsClass::Initialize(int screenWidth, int screenHeight, HWND hwnd)
{
bool result;

// Crée l'objet Direct3D.
m_D3D = new D3DClass;
if(!m_D3D)
{
return false;
}

// Initialise l'objet Direct3D.
result = m_D3D->Initialize(screenWidth, screenHeight, VSYNC_ENABLED, hwnd, FULL_SCREEN, SCREEN_DEPTH, SCREEN_NEAR);
if(!result)
{
MessageBox(hwnd, L"Could not initialize Direct3D.", L"Error", MB_OK);
return false;
}

// Crée l'objet Camera.
m_Camera = new CameraClass;
if(!m_Camera)
{
return false;
}

// Règle la position initiale de la caméra.
m_Camera->SetPosition(0.0f, 0.0f, -10.0f);

// Crée l'objet Model.
m_Model = new ModelClass;
if(!m_Model)
{
return false;
}

La méthode Initialize du modèle prend maintenant le nom du fichier du modèle qu'il charge en paramètre. Dans ce tutoriel, nous utiliserons le fichier cube.txt depuis lequel sera chargé le cube 3D pour le rendu.

// MODIFIÉ - initialise l'objet Model.
result = m_Model->Initialize(m_D3D->GetDevice(), "../Engine/data/cube.txt", L"../Engine/data/seafloor.dds");
if(!result)
{
MessageBox(hwnd, L"Could not initialize the model object.", L"Error", MB_OK);
return false;
}

// Crée l'objet shader de lumière.
m_LightShader = new LightShaderClass;
if(!m_LightShader)
{
return false;
}

// Initialise l'objet Model.
result = m_LightShader->Initialize(m_D3D->GetDevice(), hwnd);
if(!result)
{
MessageBox(hwnd, L"Could not initialize the light shader object.", L"Error", MB_OK);
return false;
}

// Crée l'objet lumière.
m_Light = new LightClass;
if(!m_Light)
{
return false;
}

Dans ce tutoriel, j'ai mis une couleur de lumière diffuse blanche.

// Initialise l'objet lumière.
m_Light->SetDiffuseColor(1.0f, 1.0f, 1.0f, 1.0f); // MODIFIÉ
m_Light->SetDirection(0.0f, 0.0f, 1.0f);

return true;
}

void GraphicsClass::Shutdown()
{
// Libère l'objet lumière.
if(m_Light)
{
delete m_Light;
m_Light = 0;
}

// Libère l'objet shader de lumière.
if(m_LightShader)
{
m_LightShader->Shutdown();
delete m_LightShader;
m_LightShader = 0;
}

// Libère l'objet Model.
if(m_Model)
{
m_Model->Shutdown();
delete m_Model;
m_Model = 0;
}

// Libère l'objet Camera.
if(m_Camera)
{
delete m_Camera;
m_Camera = 0;
}

// Libère l'objet Direct3D.
if(m_D3D)
{
m_D3D->Shutdown();
delete m_D3D;
m_D3D = 0;
}

return;
}

bool GraphicsClass::Frame()
{
bool result;
static float rotation = 0.0f;

// Met à jour l'angle de rotation lors de chaque trame.
rotation += (float)D3DX_PI * 0.01f;
if(rotation > 360.0f)
{
rotation -= 360.0f;
}

// Effectue le rendu de la scène.
result = Render(rotation);
if(!result)
{
return false;
}

return true;
}

bool GraphicsClass::Render(float rotation)
{
D3DXMATRIX worldMatrix, viewMatrix, projectionMatrix;
bool result;

// Efface les tampons pour débuter la scène.
m_D3D->BeginScene(0.0f, 0.0f, 0.0f, 1.0f);

// Génère la matrice de vue à partir de la position de la caméra.
m_Camera->Render();

// Récupère les matrices monde, de vue et de projection depuis les objets Camera et D3D.
m_Camera->GetViewMatrix(viewMatrix);
m_D3D->GetWorldMatrix(worldMatrix);
m_D3D->GetProjectionMatrix(projectionMatrix);

// Applique une rotation à la matrice monde afin de faire tourner le triangle.
D3DXMatrixRotationY(&worldMatrix, rotation);

// Place les tampons des sommets et d'indices du modèle dans le pipeline graphique pour les préparer à être rendus.
m_Model->Render(m_D3D->GetDeviceContext());

// Effectue le rendu du modèle avec le shader de lumière.
result = m_LightShader->Render(m_D3D->GetDeviceContext(), m_Model->GetIndexCount(), worldMatrix, viewMatrix, projectionMatrix,
m_Model->GetTexture(), m_Light->GetDirection(), m_Light->GetDiffuseColor());
if(!result)
{
return false;
}

// Affiche la scène rendue à l'écran.
m_D3D->EndScene();

return true;
}

VII - Résumé

Avec les modifications apportées à la classe ModelClass, nous pouvons maintenant charger des modèles 3D et effectuer leur rendu. Le format utilisé ici sert seulement pour les objets statiques de base avec éclairage, mais c'est un bon début pour appréhender le fonctionnement des modèles.

VIII - Exercices à faire

1. Recompilez le code et exécutez le programme. Vous devriez obtenir un cube en rotation avec la même texture seafloor.dds. Appuyez sur « Echap » pour quitter le programme une fois cela fait ;

2. Procurez-vous un logiciel de modélisation 3D correct(1) (on l'espère quelque chose de gratuit), créez vos propres modèles simples puis exportez-les. Commencez à regarder leur format ;

3. Écrivez un programme d'analyse syntaxique simple qui convertit les formats d'exportation au format utilisé ici. Remplacez le fichier cube.txt par votre modèle et exécutez le programme.

IX - Code source

- Projet Visual Studio 2010 : dx11tut07.zip.

- Source seule : dx11src07.zip.

- Exécutable seul : dx11exe07.zip.

X - Remerciements

Cet article est une traduction autorisée de l'article paru sur RasterTek.

Merci à LittleWhite pour sa relecture lors de la traduction et jacques_jean pour sa relecture orthographique.

Navigation

Tutoriel précédent : lumière diffuse

Sommaire

Tutoriel suivant : chargement de modèles Maya 2011

(1)NdT : on pourra consulter cette page.