Navigation▲
Tutoriel précédent : lumière diffuse |
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.0
f;
const
float
SCREEN_NEAR =
0.1
f;
///
/////////////////////////////////////////////////////////////////////////////
// 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.0
f, 0.0
f, -
10.0
f);
// 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.0
f, 1.0
f, 1.0
f, 1.0
f); // MODIFIÉ
m_Light->
SetDirection(0.0
f, 0.0
f, 1.0
f);
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.0
f;
// Met à jour l'angle de rotation lors de chaque trame.
rotation +=
(float
)D3DX_PI *
0.01
f;
if
(rotation >
360.0
f)
{
rotation -=
360.0
f;
}
// 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.0
f, 0.0
f, 0.0
f, 1.0
f);
// 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▲
- 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 ;
- 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 ;
- É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 |
Tutoriel suivant : chargement de modèles Maya 2011 |