Suite de mon premier article sur le développement de jeux en 3D, nous allons à présent découvrir comment créer un triangle dans un univers 3D. Ce dernier nous permettra d’approcher les bases de la 3D, de son initialisation à son rendu visuel.
Nous reprenons le projet créé dans l’introduction, si besoin voici ma version : MyFirstApp3D.zip.
Pour plus de confort, nous allons enlever le cube de départ.
Commencez par le fichier MyFirstApp3D.cpp, nous allons supprimer toutes les lignes qui contiennent « m_renderer » correspondant à ce fameux cube. Au total vous devriez avoir supprimé 6 lignes, ci-dessous l’ensemble du fichier après suppression :
MyFirstApp3D.cpp
#include "pch.h"
#include "MyFirstApp3D.h"
#include "BasicTimer.h"
using namespace Windows::ApplicationModel;
using namespace Windows::ApplicationModel::Core;
using namespace Windows::ApplicationModel::Activation;
using namespace Windows::UI::Core;
using namespace Windows::System;
using namespace Windows::Foundation;
using namespace Windows::Graphics::Display;
MyFirstApp3D::MyFirstApp3D() :
m_windowClosed(false)
{
}
void MyFirstApp3D::Initialize(CoreApplicationView^ applicationView)
{
applicationView->Activated +=
ref new TypedEventHandler(this, &MyFirstApp3D::OnActivated);
CoreApplication::Suspending +=
ref new EventHandler(this, &MyFirstApp3D::OnSuspending);
CoreApplication::Resuming +=
ref new EventHandler(this, &MyFirstApp3D::OnResuming);
}
void MyFirstApp3D::SetWindow(CoreWindow^ window)
{
window->SizeChanged +=
ref new TypedEventHandler(this, &MyFirstApp3D::OnWindowSizeChanged);
window->Closed +=
ref new TypedEventHandler(this, &MyFirstApp3D::OnWindowClosed);
window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
}
void MyFirstApp3D::Load(Platform::String^ entryPoint)
{
}
void MyFirstApp3D::Run()
{
BasicTimer^ timer = ref new BasicTimer();
while (!m_windowClosed)
{
timer->Update();
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
}
}
void MyFirstApp3D::Uninitialize()
{
}
void MyFirstApp3D::OnWindowSizeChanged(CoreWindow^ sender, WindowSizeChangedEventArgs^ args)
{
}
void MyFirstApp3D::OnWindowClosed(CoreWindow^ sender, CoreWindowEventArgs^ args)
{
m_windowClosed = true;
}
void MyFirstApp3D::OnActivated(CoreApplicationView^ applicationView, IActivatedEventArgs^ args)
{
CoreWindow::GetForCurrentThread()->Activate();
}
void MyFirstApp3D::OnSuspending(Platform::Object^ sender, SuspendingEventArgs^ args)
{
// Save application state after requesting a deferral. Holding a deferral
// indicates that the application is busy performing suspending operations.
// Be aware that a deferral may not be held indefinitely. After about five
// seconds, the application will be forced to exit.
SuspendingDeferral^ deferral = args->SuspendingOperation->GetDeferral();
// Insert your code here
deferral->Complete();
}
void MyFirstApp3D::OnResuming(Platform::Object^ sender, Platform::Object^ args)
{
}
IFrameworkView^ Direct3DApplicationSource::CreateView()
{
return ref new MyFirstApp3D();
}
[Platform::MTAThread]
int main(Platform::Array^)
{
auto direct3DApplicationSource = ref new Direct3DApplicationSource();
CoreApplication::Run(direct3DApplicationSource);
return 0;
}
Nous passons ensuite au second fichier, MyFirstApp3D.h contenant une seule ligne à supprimer:
CubeRenderer^ m_renderer;
A présent, nous allons pouvoir commencer à créer une nouvelle classe qui contiendra notre objet 3D. Pour ce projet, j’ai choisi d’appeler ma classe « MyFirst3DObject », vous pouvez bien sûr choisir un autre nom du moment que celui-ci n’est pas déjà utilisé

Vous vous retrouvez normalement sur le .h de votre nouvelle classe. Nous allons la faire hériter de la classe Direct3DBase permettant de manipuler des objets 3D.
#include "Direct3DBase.h"
ref class MyFirst3DObject sealed : public Direct3DBase
Après avoir hérité de Direct3DBase, nous allons alors implémenter les différentes méthodes nécessaires au bon fonctionnement de notre objet 3D:
virtual void CreateDeviceResources() override;
virtual void CreateWindowSizeDependentResources() override;
void Update(float timeTotal, float timeDelta);
virtual void Render() override;
Nous passons ensuite au fichier MyFirst3DObject.cpp pour y implémenter les méthodes déclarées précédemment dans le header de notre classe:
void MyFirst3DObject::CreateDeviceResources()
{
Direct3DBase::CreateDeviceResources();
}
void MyFirst3DObject::CreateWindowSizeDependentResources()
{
Direct3DBase::CreateWindowSizeDependentResources();
}
void MyFirst3DObject::Update(float timeTotal, float timeDelta)
{
}
void MyFirst3DObject::Render()
{
}
Nous retournons ensuite dans MyFirstApp3D.h pour déclarer notre tout nouvel objet 
On remplace l’include CubeRenderer.h par MyFirst3DObject.h comme ceci:
#include "MyFirst3DObject.h"
Puis nous déclarons notre objet dans la partie private comme ceci:
private:
MyFirst3DObject^ m_3DObject;
Vous avez peut-être remarqué le signe ^ après le type de notre objet?
Il est nécessaire d’utiliser ce signe ainsi que « ref new » lorsque nous utilisons des objets appartenant à Windows Runtime. Il sert à manager automatiquement la durée de vie de vos objets Windows Runtime permettant de diminuer les fuites de mémoire.
Enfin, nous passons à MyFirstApp3D.cpp où nous allons instancier et gérer notre objet.
Si vous avez bien suivi mon introduction dans mon premier article vous devriez sans aucun problème réussir à compléter ce fichier. Sinon, voici un petit cours de rattrapage :
Nous commençons par la méthode Initialize pour instancier notre objet (celle-là était facile je vous l’accorde
)
m_3DObject = ref new MyFirst3DObject();
On continue par initialiser notre objet cette fois-ci dans la méthode SetWindow.
Une fois que notre fenêtre est créée et qu’elle a la bonne dimension, nous allons pouvoir donner à notre objet les informations dont il a besoin pour être correctement initialisé.
Nous initialisons notre objet avec sa propre méthode Initialize en lui passant en argument la fenêtre du thread actif (avec CoreWindow::GetForCurrentThread())
window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0);
m_3DObject->Initialize(CoreWindow::GetForCurrentThread());
Enfin, nous nous rendons dans la méthode Run pour mettre à jour et afficher notre objet.
void MyFirstApp3D::Run()
{
BasicTimer^ timer = ref new BasicTimer();
while (!m_windowClosed)
{
timer->Update();
CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);
m_3DObject->Update(timer->Total, timer->Delta);
m_3DObject->Render();
m_3DObject->Present();
}
}
Maintenant que la structure de notre projet est posée, nous allons enfin pouvoir passer à la création de notre premier triangle.
L’ensemble de sa création se passera dans la classe MyFirst3DObject. Nous allons commencer par préparer l’ossature de notre triangle avec ses vertices et indices, nous lui donnerons une couleur et pour terminer nous l’afficherons.
Commençons par définir ce qu’est un vertex (des vertices):
Un « vertex » se traduit par un « sommet » en français, ce dernier étant un point d’une figure géométrique. Ainsi, une ligne se compose de deux sommets, et un triangle de trois!
On continue par définir ce qu’est un index (des indices):
Un indice va correspondre à un numéro donné à un sommet (Cet indice est défini lorsque chaque sommet est lui-même défini: le premier sommet défini obtiendra l’indice 0). L’indice va ensuite servir à déterminer l’ordre dans lequel chaque sommet doit être dessiné.
Un petit schéma pour expliquer tout ça :

Nous avons un triangle composé de trois vertices chacun ayant des coordonnées.
Le premier point se trouve aux coordonnées -0,5 sur l’axe des abscisses et 0,0 sur l’axe des ordonnées. Etant le premier, il se voit attribuer l’indice 0 et ainsi de suite jusqu’au dernier point.
Maintenant que les présentations sont faites, nous allons pouvoir commencer à rentrer un peu plus dans le code
Rendez-vous dans la méthode que nous avons implémenté un peu plus tôt: CreateDeviceResources().
Cette méthode contient déjà une ligne de code qui appelle CreateDeviceRessources de l’objet Direct3DBase.
Cette dernière méthode va déclarer les versions de DirectX que votre application supporte mais va aussi initialiser les objets m_d3dDevice et m_d3dContext.
Ces deux objets sont respectivement (et très globalement) l’objet gérant votre écran et l’objet gérant les éléments à afficher sur ce dernier.
Avant de continuer, je pense qu’il serait bien de se concentrer un peu plus en détail sur le Device et le Context (ou plutôt DeviceContext).
Comme je l’ai dit, le Device va gérer votre écran. En fait, cet objet est utilisé pour créer des ressources et définir les capacités techniques qu’offre votre écran.
Le Device va contenir un à plusieurs DeviceContext. Le premier est ce qu’on appelle le Context Immédiat (ou Immediate Context en anglais), il va afficher directement à l’écran ce qu’on lui fournit. Un Device ne peut avoir qu’un seul Immediate Context. Il existe aussi le Context Différé (ou Deferred Context), celui-ci est utilisé principalement pour faire du multithreading.
Nous allons charger un fichier déjà présent dans la solution de base, il s’agit de SimpleVertexShader.hlsl.
Avant de le charger, nous allons modifier ce dernier. (Actuellement, ce fichier est configuré pour fonctionner avec un univers 3D, le but de cet article étant de voir comment concevoir un premier triangle, nous n’allons pas directement travailler dans cet univers).
Voici le fichier SimpleVertexShader.hlsl une fois modifié (Il vous suffit de remplacer l’ensemble du contenu de votre fichier par le code ci-dessous):
//structure contenant la position en entrée
//possédant deux coordonnées x et y
//pos contiendra les coordonnées des Vertices que nous enverrons
struct VertexShaderInput
{
  float2 pos : POSITION;
};
//structure contenant la position transformée en 3D (ajout de la profondeur (0.5f))Â
struct VertexShaderOutput
{
  float4 pos : SV_POSITION;
};
VertexShaderOutput main(VertexShaderInput input)
{
  VertexShaderOutput output;
  output.pos = float4(input.pos, 0.5f, 1.0f);
  return output;
}
Nous pouvons à présent le charger à la suite de Direct3DBase::CreateDeviceResources(); puis créer notre VertexShader :
//création de la tâche asynchrone dédiée à la lecture du fichier SimpleVertexShader
auto loadVSTask = DX::ReadDataAsync("SimpleVertexShader.cso");
//lancement de la tâche asynchrone loadVSTask
loadVSTask.then([this](DX::ByteArray ba){
//exécution du code suivant lorsque le chargement du fichier est terminé
auto bytecodeVS = ba.data;
//création du vertexShader avec le fichier précédemment chargé
DX::ThrowIfFailed(
m_d3dDevice->CreateVertexShader(
bytecodeVS->Data,
bytecodeVS->Length,
nullptr,
&m_vertexShader
)
);
});
Nous pouvons rajouter par la même occasion dans le header de notre classe la déclaration de l’objet m_vertexShader qui va contenir notre VertexShader.
Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertexShader;
La tâche du VertexShader consiste entre autres à bien positionner, colorer les sommets de la scène 3D sur votre écran (transformation des positions 3D en position 2D).
Regardons de plus près la méthode qui construit notre VertexShader.
La méthode CreateVertexShader nécessite 4 arguments:
- Le premier correspond aux données compilées du shader
- Le second correspond à la taille de ses données
- Le troisième correspond à un objet de type ID3D11ClassLinkage, ce troisième argument ne nous intéresse pas pour le moment, nous pouvons le laisser à null.
- Le dernier correspond quant à lui à l’objet qui contiendra le VertexShader.
Nous allons rajouter la création de l’InputLayout juste après la création du VertexShader.
Un InputLayout va globalement contenir une définition des données qui peuvent être fournies pour dessiner les vertices, pour cet exemple nous allons simplement dire que nous fournirons une position 2D des vertices.
Voici l’ensemble du code commenté concernant le fichier SimpleVertexShader de sa lecture à l’implémentation de l’InputLayout:
//création de la tâche asynchrone dédiée à la lecture du fichier SimpleVertexShader
auto loadVSTask = DX::ReadDataAsync("SimpleVertexShader.cso");
//lancement de la tâche asynchrone loadVSTask
loadVSTask.then([this](DX::ByteArray ba){
//exécution du code suivant lorsque le chargement du fichier est terminé
auto bytecodeVS = ba.data;
//création du vertexShader avec le fichier précédemment chargé
DX::ThrowIfFailed(
m_d3dDevice->CreateVertexShader(
bytecodeVS->Data,
bytecodeVS->Length,
nullptr,
&m_vertexShader
)
);
//définition des données que nous renseignerons à nos vertices
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
//création de l'inputLayout avec le tableau de définition précédemment créé
DX::ThrowIfFailed(
m_d3dDevice->CreateInputLayout(
vertexDesc, //tableau de définition
ARRAYSIZE(vertexDesc),//taille du tableau
bytecodeVS->Data,//données compilées du shader
bytecodeVS->Length,//taille des données compilées
&m_inputLayout//objet de sortie
)
);
});
A nouveau, nous rajoutons la déclaration de l’objet qui contiendra l’InputLayout dans le header de la classe.
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
Nous allons à présent charger le fichier SimplePixelShader.hlsl, même procédure que précédemment, nous apportons quelques modifications avant de le charger:
struct PixelShaderInput
{
float4 pos : SV_POSITION;
};
float4 main(PixelShaderInput input) : SV_TARGET
{
//au lieu de récupérer les données renseignées, nous affectons manuellement une couleur
//les trois premiers arguments correspondent aux couleur rouge, vert et bleu
//pour obtenir du bleu, il faut donc renseigner dans l'ordre 0, 0, 1
return float4(0.0f,0.0f,1.0f,1.0f);
}
Toujours dans la même méthode (CreateDeviceResources()), nous allons ajouter à la suite de notre code le chargement du fichier SimplePixelShader ainsi que la création de notre PixelShader:
//création de la tâche asynchrone dédiée à la lecture du fichier SimplePixelShader
auto loadPSTask = DX::ReadDataAsync("SimplePixelShader.cso");
//lancement de la tâche asynchrone loadPSTask
auto createPSTask = loadPSTask.then([this](DX::ByteArray ba) {
auto bytecodePS = ba.data;
DX::ThrowIfFailed(
m_d3dDevice->CreatePixelShader(
bytecodePS->Data,//données compilées du shader
bytecodePS->Length,//taille des données compilées
nullptr, //classe de linkage (non utilisé pour le moment)
&m_pixelShader //objet de sortie
)
);
});
Et nous ajoutons dans le header de notre classe la déclaration de m_pixelShader:
 Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixelShader;
Le PixelShader quant à lui va s’occuper du rendu de chaque pixel de vos objets, il va entre autres choses réaliser des calculs pour colorer, éclairer vos pixels.
Maintenant que les shaders sont créés, il nous reste à définir les propriétés de l’objet que nous voulons créer ! Vous vous souvenez du petit schéma que je vous ai montré un peu plus haut ? On va s’en inspirer pour créer notre triangle
Pour rappel, les coordonnées des sommets de ce triangle étaient:
- 0 : {-0.5;0.0}
- 1 : { Â 0.0;0.5}
- 2 : { Â 0.5;0.0}
Nous allons le retranscrire en code, toujours dans la même méthode, à la suite de la création de notre PixelShader:
XMFLOAT2 vertices[] =
{
XMFLOAT2(-0.5f,0.0f),
XMFLOAT2( 0.0f, 0.5f),
XMFLOAT2( 0.5f, 0.0f),
};
Pas très compliqué cette déclaration, on notera quand même le type utilisé pour déclarer ce tableau XMFLOAT2.
Ce type est utilisé pour combiner deux float en une seule variable, il existe aussi XMFLOAT3 et bien d’autres.
Pour pouvoir l’utiliser, nous devons utiliser le namespace DirectX.
Il faut tout simplement le déclarer en haut de notre fichier juste après l’include du header:
using namespace DirectX;
Nous allons ensuite créer le VertexBuffer directement sous la déclaration de nos vertices :
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = vertices; //enregistrement des vertices pour la création du VertexBuffer
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
//description du contenu du buffer, on fourni la taille du tableau de vertices et on indique que nous voulons faire un VertexBuffer
&CD3D11_BUFFER_DESC(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER),
&vertexBufferData,//on donne le tableau de vertices précédemment enregistré
&m_vertexBuffer //et on stocke le tout dans m_vertexBuffer
)
);
Encore une fois on déclare m_vertexBuffer dans notre header :
Microsoft::WRL::ComPtr<ID3D11Buffer> m_vertexBuffer;
Le VertexBuffer va contenir un tableau de l’ensemble des vertices de votre espace 3D. Au lieu d’envoyer ce tableau à chaque fois que nous voulons un rendu 3D, nous allons l’envoyer dès le début à la carte graphique pour alléger le transfert de données. Ainsi la carte graphique aura en mémoire tous les vertices dont elle à besoin pour créer un rendu de votre scène.
Nous allons enfin terminer l’initialisation de notre scène avec les indices
(à  la suite de la déclaration de notre VertexBuffer)
Vu que nous n’avons que 3 sommets, la déclaration des indices sera assez rapide:
unsigned short indices[] =
{
0,1,2,
};
Nous gardons ensuite en mémoire le nombre d’indices créés pour une future utilisation et nous créons l’indexBuffer de la même manière que le VertexBuffer:
m_indexCount = ARRAYSIZE(indices);
D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = indices; //enregistrement des indices pour la création de l'IndexBuffer
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
//description du contenu du buffer, on fourni la taille du tableau d'indices et on indique que nous voulons faire un IndexBuffer
&CD3D11_BUFFER_DESC(sizeof(indices), D3D11_BIND_INDEX_BUFFER),
&indexBufferData,//on donne le tableau d'indices précédemment enregistré
&m_indexBuffer //et on stocke le tout dans m_indexBuffer
)
);
Et on déclare m_indexBuffer ainsi que m_indexCount dans le header de la classe:
Microsoft::WRL::ComPtr<ID3D11Buffer> m_indexBuffer;
int m_indexCount;
L’IndexBuffer va optimiser votre application. Dans notre cas, il ne va pas être grandement utile mais prenons l’exemple d’un simple carré. Etant donné qu’on ne peut créer que des triangles, un carré sera composé de deux triangles. Un triangle possède 3 sommets, le carré contiendra alors 6 sommets. Je pense que vous voyez où je veux en venir. Plus vos objets seront complexes et plus ils contiendront beaucoup de sommets souvent inutiles. L’IndexBuffer va être là pour parer à ce genre de phénomène. Il va fusionner les sommets se trouvant aux mêmes positions et optimisera donc le nombre de sommet à afficher. Le carré aura donc 4 sommets comme il se doit.
Si vous êtes arrivé jusque-là , je vous garantis que vous avez fait le plus dur! Bravo
Il ne nous reste plus qu’à ajouter le tout dans la méthode render() pour afficher notre triangle:
void MyFirst3DObject::Render()
{
//création d'une couleur blanche
const float white[] = { 1.0f, 1.0f, 1.0f, 1.000f };
//on nettoie l'écran avec la couleur définie ci-dessus
m_d3dContext->ClearRenderTargetView(
m_renderTargetView.Get(),
white
);
m_d3dContext->ClearDepthStencilView(
m_depthStencilView.Get(),
D3D11_CLEAR_DEPTH,
1.0f,
0
);
//établit la cible de rendu avec l'écran précédemment nettoyé
m_d3dContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
m_depthStencilView.Get()
);
//on place l'ensemble des vertices sur l'écran
UINT stride = sizeof(XMFLOAT2);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset
);
//on charge les indices
m_d3dContext->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0
);
//on indique la topologie à utiliser
m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//on charge l'InputLayout
m_d3dContext->IASetInputLayout(m_inputLayout.Get());
//on charge le VertexShader
m_d3dContext->VSSetShader(
m_vertexShader.Get(),
nullptr,
0
);
//on charge le PixelShader
m_d3dContext->PSSetShader(
m_pixelShader.Get(),
nullptr,
0
);
//Enfin nous dessinons le tout
m_d3dContext->DrawIndexed(
m_indexCount,
0,
0
);
}
Je pense que les commentaires parlent d’eux-mêmes, je reviendrais toutefois sur la topologie utilisée.
Ce type de topologie va aider à interpréter d’une certaine façon les différents vertex que nous fournissons. Il existe différentes topologies énumérées ici.
La topologie Triangle List permet de créer un triangle à chaque trois vertex.
On aura par exemple une autre topologie, Triangle Strip, qui permet, une fois le premier triangle de créé, d’ajouter un triangle à chaque fois que nous ajoutons un vertex.
A présent, vous pouvez enfin exécuter votre code et admirer.. un seul triangle sur un fond uni:

Je vous mets à disposition le code source de l’objet créé dans cet article, vous pouvez aussi récupérer l’ensemble du projet de cet article ici.
MyFirst3DObject.h
#pragma once
#include "Direct3DBase.h"
ref class MyFirst3DObject sealed : public Direct3DBase
{
public:
MyFirst3DObject(void);
~MyFirst3DObject(void);
virtual void CreateDeviceResources() override;
virtual void CreateWindowSizeDependentResources() override;
void Update(float timeTotal, float timeDelta);
virtual void Render() override;
private:
Microsoft::WRL::ComPtr<ID3D11VertexShader> m_vertexShader;
Microsoft::WRL::ComPtr<ID3D11InputLayout> m_inputLayout;
Microsoft::WRL::ComPtr<ID3D11PixelShader> m_pixelShader;
Microsoft::WRL::ComPtr<ID3D11Buffer> m_vertexBuffer;
Microsoft::WRL::ComPtr<ID3D11Buffer> m_indexBuffer;
int m_indexCount;
};
MyFirst3DObject.cpp
#include "pch.h"
#include "MyFirst3DObject.h"
using namespace DirectX;
MyFirst3DObject::MyFirst3DObject(void)
{
}
MyFirst3DObject::~MyFirst3DObject(void)
{
}
void MyFirst3DObject::CreateDeviceResources()
{
Direct3DBase::CreateDeviceResources();
//création de la tâche asynchrone dédiée à la lecture du fichier SimpleVertexShader
auto loadVSTask = DX::ReadDataAsync("SimpleVertexShader.cso");
//lancement de la tâche asynchrone loadVSTask
loadVSTask.then([this](DX::ByteArray ba){
//exécution du code suivant lorsque le chargement du fichier est terminé
auto bytecodeVS = ba.data;
//création du vertexShader avec le fichier précédemment chargé
DX::ThrowIfFailed(
m_d3dDevice->CreateVertexShader(
bytecodeVS->Data,
bytecodeVS->Length,
nullptr,
&m_vertexShader
)
);
//définition des données que nous renseignerons à nos vertices
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
//création de l'inputLayout avec le tableau de définition précédemment créé
DX::ThrowIfFailed(
m_d3dDevice->CreateInputLayout(
vertexDesc, //tableau de définition
ARRAYSIZE(vertexDesc),//taille du tableau
bytecodeVS->Data,//données compilées du shader
bytecodeVS->Length,//taille des données compilées
&m_inputLayout//objet de sortie
)
);
});
//création de la tâche asynchrone dédiée à la lecture du fichier SimplePixelShader
auto loadPSTask = DX::ReadDataAsync("SimplePixelShader.cso");
//lancement de la tâche asynchrone loadPSTask
auto createPSTask = loadPSTask.then([this](DX::ByteArray ba) {
auto bytecodePS = ba.data;
DX::ThrowIfFailed(
m_d3dDevice->CreatePixelShader(
bytecodePS->Data,//données compilées du shader
bytecodePS->Length,//taille des données compilées
nullptr,
&m_pixelShader //objet de sortie
)
);
});
XMFLOAT2 vertices[] =
{
XMFLOAT2(-0.5f,0.0f),
XMFLOAT2( 0.0f, 0.5f),
XMFLOAT2( 0.5f, 0.0f),
};
D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
vertexBufferData.pSysMem = vertices; //enregistrement des vertices pour la création du VertexBuffer
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
//description du contenu du buffer, on fourni la taille du tableau de vertices et on indique que nous voulons faire un VertexBuffer
&CD3D11_BUFFER_DESC(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER),
&vertexBufferData,//on donne le tableau de vertices précédemment enregistré
&m_vertexBuffer //et on stocke le tout dans m_vertexBuffer
)
);
unsigned short indices[] =
{
0,1,2,
};
m_indexCount = ARRAYSIZE(indices);
D3D11_SUBRESOURCE_DATA indexBufferData = {0};
indexBufferData.pSysMem = indices; //enregistrement des indices pour la création de l'IndexBuffer
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
//description du contenu du buffer, on fourni la taille du tableau d'indices et on indique que nous voulons faire un IndexBuffer
&CD3D11_BUFFER_DESC(sizeof(indices), D3D11_BIND_INDEX_BUFFER),
&indexBufferData,//on donne le tableau d'indices précédemment enregistré
&m_indexBuffer //et on stocke le tout dans m_indexBuffer
)
);
}
void MyFirst3DObject::CreateWindowSizeDependentResources()
{
Direct3DBase::CreateWindowSizeDependentResources();
}
void MyFirst3DObject::Update(float timeTotal, float timeDelta)
{
}
void MyFirst3DObject::Render()
{
//création d'une couleur blanche
const float white[] = { 1.0f, 1.0f, 1.0f, 1.000f };
//on nettoie l'écran avec la couleur définie ci-dessus
m_d3dContext->ClearRenderTargetView(
m_renderTargetView.Get(),
white
);
m_d3dContext->ClearDepthStencilView(
m_depthStencilView.Get(),
D3D11_CLEAR_DEPTH,
1.0f,
0
);
//établit la cible de rendu avec l'écran précédemment nettoyé
m_d3dContext->OMSetRenderTargets(
1,
m_renderTargetView.GetAddressOf(),
m_depthStencilView.Get()
);
//on place l'ensemble des vertices sur l'écran
UINT stride = sizeof(XMFLOAT2);
UINT offset = 0;
m_d3dContext->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset
);
//on charge les indices
m_d3dContext->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT,
0
);
//on indique la topologie à utiliser
m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//on charge l'InputLayout
m_d3dContext->IASetInputLayout(m_inputLayout.Get());
//on charge le VertexShader
m_d3dContext->VSSetShader(
m_vertexShader.Get(),
nullptr,
0
);
//on charge le PixelShader
m_d3dContext->PSSetShader(
m_pixelShader.Get(),
nullptr,
0
);
//Enfin nous dessinons le tout
m_d3dContext->DrawIndexed(
m_indexCount,
0,
0
);
}
Maintenant que vous savez comment intégrer un tout premier triangle dans une application Windows 8, vous êtes prêt à attaquer de la vraie 3D avec
mon prochain articleÂ
Login