Archives pour la catégorie DirectX

Le Compute Shader avec DirectX 11 (DirectCompute)

GPU

Intro :

Pour faire simple, le compute shader de DirectX 11 permet d’effectuer des calculs directement par le processeur de la carte graphique (nommé GPU) alors qu’ils auraient pu être fait par le CPU (le processeur de la carte-mère du PC). Il est dénommé par un autre nom : le DirectCompute.

A noter que bien que leur sortie s’est faite avec DirectX 11, on peut utiliser les compute shader avec du matériel DirectX 10.

Explications :

Le compute shader permet le partage de la mémoire entre processus légers synchronisés à l’intérieure de la CG et de la mémoire de l’ordinateur. Il utilise le langage HLSL (High Level Shader Language) classique de DirectX.

En théorie on peut calculer n’importe quoi. Le compute shader fait office d’accès mémoire en entrée / sortie par les programmes exécutés par le processeur.

Maintenant et depuis l’ajout de cette fonctionnalité, avec DirectX on peut faire du calcul non graphique en utilisant la CG.


Voici un petit exemple d’initialisation d’un compute shader :

UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
flags |= D3DCOMPILE_DEBUG;
#endif

// Prefer higher CS shader profile when possible as CS 5.0 provides better performance on 11-class hardware.
LPCSTR profile = ( device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0 ) ? "cs_5_0" : "cs_4_0";

const D3D_SHADER_MACRO defines[] = 
{
     "EXAMPLE_DEFINE", "1",
      NULL, NULL
};

ID3DBlob* shaderBlob = nullptr;
ID3DBlob* errorBlob = nullptr;

HRESULT hr = D3DCompileFromFile( srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
                                     entryPoint, profile,
                                     flags, 0, &shaderBlob, &errorBlob );

– Avec srcFile comme le nom du fichier .fx d’un compute shader.
– Avec defines comme structure de déclaration des constantes defines à l’intérieur de l’effet.

Quand le compute shader sera exécuté dans la mémoire de la carte graphique, il pourra tirer avantage d’un grand nombre d’unités d’exécution parallèles à l’intérieur du GPU.

Qu’est-ce qu’un « thread group » ?

Un thread group est un groupe d’unités d’exécution en parallèle qui est interprété et conçu par la CG comme un tableau en 3 dimensions dont chaque membre est un « thread ».

On utilise la méthode ID3D11DeviceContext::Dispatch pour envoyer un « thread group » au GPU. C’est-à-dire qu’on utilise cette méthode pour faire exécuter des commandes un compute shader.

En effet un compute shader peut être exécuté sur plusieurs unité d’exécution en parallèle, le tout formant un « thread group ». On identifie un thread (unité d’exécution parallèle) en particulier avec un numéro d’index assimilé par un vecteur en 3 dimensions (x, y, z).

Dans cette image illustrant un exemple, la taille des « threads groups » est de 5x5x2, soit 50 groupes d’unités d’exécution en parallèle (« thread »). Ici un thread en particulier est adressé par le triplet (4,1,1). A noter que chaque groupe de threads est fait de 16×16 = 256 unité ou threads d’exécution.

Single Thread

Chaque processus léger (« thread ») peut exécuter plusieurs élément de données à travers le compute shader. Et l’application C++ qui gère tous ces calculs peut définir le nombre de threads en exécution.


La principale capacité des compute shaders est le partage et l’accès de la mémoire par plusieurs threads en parallèle.

Le principe de base du calcul par le biais de ces shaders est l’envoi régulier d’une grille de threads. Les threads d’un même groupe s’exécute en parallèle, mais des threads de groupes différents le peuvent dans certaines conditions.

Les données accessibles par le compute shader peuvent être les Buffers et Textures de DirectX 11.


Les applications au compute shader peuvent être :

– Le traitement d’images
– La construction procédurale de modèles
– La physique et l’animation
– Le rendu
– L’intelligence artificielle
– Les effets de particules
– Les algorithmes de tri
– L’encodage vidéo
– Le calcul scientifique
– Des effets graphiques tels que : le depth of field, HDR bloom, le motion blur, etc…

Une classe TextureScreenQuad pour afficher vos effets shaders de Post-Traitement

fig-1-war-and-peace

Intro :

Dans un jeu, nous avons besoin d’afficher une texture / image qui prend la place de tout l’écran. Cette image peut être modifiée pour appliquer un rendu de post-effet avec un fichier shader .fx.

Explications :

Nous nous servons d’un tampon de vertices formant un carré ou d’un quad.

Voici le fichier TextureScreenQuad.h :

#ifndef TEXTURE_QUAD_H
#define TEXTURE_QUAD_H

#include <d3dx10.h>
#include <string>

#include "VertexDeclarations.h"
#include "Renderable.h"
#include "RenderTarget.h"
#include "MeshSceneNode.h"

class TextureScreenQuad : public MeshSceneNode
{
public:
    TextureScreenQuad();
    virtual ~TextureScreenQuad();

    bool Initialize();
    virtual void OnRender(float fTimeSinceLastFrame) override;

    void SetTransparency(float fValue);
    void SetColor(const D3DXCOLOR& color);
    void SetQuadSize(uint32 iWidth, uint32 iHeight);

    RenderTarget* GetRT();

    virtual bool SetShaderTechnique(ShaderTechnique* pShader) override;

protected:
    bool Update();

private:
    int m_iScreenWidth;
    int m_iScreenHeight;

    ID3D10ShaderResourceView* m_pTextureRessourceView;

    float m_fAlpha;

    PTVertex m_aQuadVertices[6];

    RenderTarget* m_pRT;
    D3DXCOLOR m_Color;
};

#endif

 

Voici le fichier TextureScreenQuad.cpp :

#include "TextureScreenQuad.h"
#include "Defines.h"
#include "D3D10Renderer.h"
#include "ShaderTechnique.h"
#include "ShaderTechnique_Declarations.h"

TextureScreenQuad::TextureScreenQuad() :
MeshSceneNode("TextureScreenQuad"),
m_fAlpha(1.0f),
m_pRT(nullptr)
{
    ZeroMemory(&m_aQuadVertices, sizeof(PTVertex) * 6);
    SetDrawMethod(DRAW_INDEXED);
    SetVertexType(VertexLayoutType::PT_VERTEX);
}

TextureScreenQuad::~TextureScreenQuad()
{
    SAFE_RELEASE(m_pTextureRessourceView);
}

bool TextureScreenQuad::Initialize()
{
    m_iScreenWidth = D3D10_RENDERER->GetViewportWidth();
    m_iScreenHeight = D3D10_RENDERER->GetViewportHeight();

    if (!Update())
    {
        MessageBoxA(nullptr, "Erreur d'initialisation d'un TextureScreenQuad !", "Erreur", MB_ICONHAND | MB_OK);
    }

    return true;
}

void TextureScreenQuad::SetTransparency(float fValue)
{
    m_Color.a = fValue;

    if (GetShaderTechnique() != nullptr)
    {
        GetShaderTechnique()->SetColor("Color", m_Color);
    }
}

void TextureScreenQuad::SetColor(const D3DXCOLOR& color)
{
    m_Color = color;
    
    if (GetShaderTechnique() != nullptr)
    {
        GetShaderTechnique()->SetColor("Color", m_Color);
    }
}

void TextureScreenQuad::OnRender(float fTimeSinceLastFrame)
{
    if (IsVisible())
    {
        D3D10_RENDERER->EnableZBuffer(false);

            Renderable::OnRender(fTimeSinceLastFrame);

        D3D10_RENDERER->EnableZBuffer(true);
    }
}

bool TextureScreenQuad::Update()
{
    float left, right, top, bottom;

    left = (float) -m_iScreenWidth / 2;
    right = left + (float) m_iScreenWidth;

    top = (float) -m_iScreenHeight / 2;
    bottom = top + (float) m_iScreenHeight;

    // Premier triangle
    m_aQuadVertices[0].pos = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    m_aQuadVertices[0].texture = D3DXVECTOR2(0.0f, 0.0f);

    m_aQuadVertices[1].pos = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    m_aQuadVertices[1].texture = D3DXVECTOR2(1.0f, 1.0f);

    m_aQuadVertices[2].pos = D3DXVECTOR3(left, bottom, 0.0f);  // Bottom left.
    m_aQuadVertices[2].texture = D3DXVECTOR2(0.0f, 1.0f);

    // Deuxième triangle
    m_aQuadVertices[3].pos = D3DXVECTOR3(left, top, 0.0f);  // Top left.
    m_aQuadVertices[3].texture = D3DXVECTOR2(0.0f, 0.0f);

    m_aQuadVertices[4].pos = D3DXVECTOR3(right, top, 0.0f);  // Top right.
    m_aQuadVertices[4].texture = D3DXVECTOR2(1.0f, 0.0f);

    m_aQuadVertices[5].pos = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
    m_aQuadVertices[5].texture = D3DXVECTOR2(1.0f, 1.0f);

    // On déclare les tableaux
    std::vector<PTVertex> vertices;
    // On reset les emplacements
    vertices.resize(6);
    // On affecte les valeurs
    std::copy(m_aQuadVertices, m_aQuadVertices + 6, vertices.begin());

    unsigned short i[6] = { 0, 1, 2, 3, 4, 5 };

    // On déclare les tableaux
    std::vector<unsigned short> indices;
    // On reset les emplacements
    indices.resize(6);
    // On affecte les valeurs
    std::copy(i, i + 6, indices.begin());

    BuildMesh(vertices, indices);

    return true;
}

void TextureScreenQuad::SetQuadSize(uint32 iWidth, uint32 iHeight)
{
    m_iScreenWidth = iWidth;
    m_iScreenHeight = iHeight;

    Update();
}

RenderTarget* TextureScreenQuad::GetRT()
{
    return m_pRT;
}

bool TextureScreenQuad::SetShaderTechnique(ShaderTechnique* pShader)
{
    bool bSuccess = Renderable::SetShaderTechnique(pShader);

    if (m_pRT == nullptr)
    {
        m_pRT = new RenderTarget();
        m_pRT->Initialize(m_iScreenWidth, m_iScreenHeight);

        ShaderTechnique_FXAA* pFXAA = dynamic_cast<ShaderTechnique_FXAA*> (GetShaderTechnique());
        pFXAA->SetRenderTarget(m_pRT);

        SetColor(D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f));
    }

    return bSuccess;
}


Résumé :

Nous avons présenté une façon d’utiliser un Quad de de post-traitement afin de modifier les couleurs de rendu du front buffer.

Références :

– Stackoverflow.com

Une classe RenderTarget pour afficher un tampon d’image de rendu

rtt

Intro :

Nous avons parfois besoin de stocker le rendu 3D dans une texture que l’on pourra utiliser n’importe tout dans le programme !

Explications :

Voici le fichier RenderToTexture.h :

#ifndef RENDER_TO_TEXTURE_H
#define RENDER_TO_TEXTURE_H

#include <d3d10.h>
#include "Types.h"

class RenderTarget
{
public:
    RenderTarget();
    virtual ~RenderTarget();

    bool Initialize(uint32 iTextureWidth, uint32 iTextureHeight);

    void SetRenderTarget(ID3D10DepthStencilView* pDepthStencilView);
    void ClearRenderTarget(ID3D10DepthStencilView* pDepthStencilView);

    ID3D10ShaderResourceView* GetShaderResourceView();

private:
    ID3D10Texture2D* m_pRenderTargetTexture;
    ID3D10RenderTargetView* m_pRenderTargetView;
    ID3D10ShaderResourceView* m_pShaderResourceView;
};

#endif

 

Voici le fichier RenderToTexture.cpp :

#include "Defines.h"
#include "RenderTarget.h"
#include "D3D10Renderer.h"

RenderTarget::RenderTarget() :
m_pRenderTargetTexture(nullptr),
m_pRenderTargetView(nullptr),
m_pShaderResourceView(nullptr)
{
}

RenderTarget::~RenderTarget()
{
    SAFE_RELEASE(m_pRenderTargetTexture);
    SAFE_RELEASE(m_pRenderTargetView);
    SAFE_RELEASE(m_pShaderResourceView);
}

bool RenderTarget::Initialize(uint32 iTextureWidth, uint32 iTextureHeight)
{
    /* On créé les descriptions de texture 2D, de la render target et de la ressource du shader */
    D3D10_TEXTURE2D_DESC textureDesc;
    D3D10_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
    D3D10_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;

    HRESULT hr;
    // Initialize the render target texture description.
    ZeroMemory(&textureDesc, sizeof(textureDesc));

    // Setup the render target texture description.
    textureDesc.Width = iTextureWidth;
    textureDesc.Height = iTextureHeight;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D10_USAGE_DEFAULT;
    textureDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
    textureDesc.CPUAccessFlags = 0;
    textureDesc.MiscFlags = 0;

    // Create the render target texture.
    hr = D3D10_RENDERER->GetDevice()->CreateTexture2D(&textureDesc, NULL, &m_pRenderTargetTexture);
    if (FAILED(hr))
    {
        return false;
    }

    // Setup the description of the render target view.
    renderTargetViewDesc.Format = textureDesc.Format;
    renderTargetViewDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
    renderTargetViewDesc.Texture2D.MipSlice = 0;

    // Create the render target view.
    hr = D3D10_RENDERER->GetDevice()->CreateRenderTargetView(m_pRenderTargetTexture, &renderTargetViewDesc, &m_pRenderTargetView);
    if (FAILED(hr))
    {
        return false;
    }

    // Setup the description of the shader resource view.
    shaderResourceViewDesc.Format = textureDesc.Format;
    shaderResourceViewDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
    shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
    shaderResourceViewDesc.Texture2D.MipLevels = 1;

    // Create the shader resource view.
    hr = D3D10_RENDERER->GetDevice()->CreateShaderResourceView(m_pRenderTargetTexture, &shaderResourceViewDesc, &m_pShaderResourceView);
    if (FAILED(hr))
    {
        return false;
    }

    return true;
}

void RenderTarget::SetRenderTarget(ID3D10DepthStencilView* pDepthStencilView)
{
    D3D10_RENDERER->GetDevice()->OMSetRenderTargets(1, &m_pRenderTargetView, pDepthStencilView);
}

void RenderTarget::ClearRenderTarget(ID3D10DepthStencilView* pDepthStencilView)
{
    float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };

    // On efface le back buffer
    D3D10_RENDERER->GetDevice()->ClearRenderTargetView(m_pRenderTargetView, color);

    // On efface le depth buffer
    D3D10_RENDERER->GetDevice()->ClearDepthStencilView(pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0);
}

ID3D10ShaderResourceView* RenderTarget::GetShaderResourceView()
{
    return m_pShaderResourceView;
}

 

Comment l’utiliser ?

void D3D10Renderer::Render()
{
    [...]
    
    // Efface la surface de rendu
    m_pd3dDevice->ClearDepthStencilView(m_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0 );
    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);

    SCENE_MANAGER->DrawAll(fTimeSinceLastFrame);

    RenderToTexture(fTimeSinceLastFrame);

    m_pQuad->OnRender(fTimeSinceLastFrame);

    m_pSwapChain->Present(0, 0);
}

void D3D10Renderer::RenderToTexture(float fTimeSinceLastFrame)
{
    // Set the render target to be the render to texture.
    m_pQuad->GetRT()->SetRenderTarget(m_pDepthStencilView);

    // Clear the render to texture.
    m_pQuad->GetRT()->ClearRenderTarget(m_pDepthStencilView);

    // Render the scene now and it will draw to the render to texture instead of the back buffer.
    SCENE_MANAGER->DrawAll(fTimeSinceLastFrame);

    // Reset the render target back to the original back buffer and not the render to texture anymore.
    m_pd3dDevice->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);
}

 

Résumé :

Nous avons présenter la façon la plus optimale possible d’utiliser une RenderTarget !

Références :

Une simple classe D3D10Renderer

Intro :

1211804249

Pour utiliser DirectX, il nous faut une classe D3D10Renderer afin d’initialiser le rendu graphique 3D.

Prérequis :

– Aucun

Explications :

Voici le code du fichier D3D10Renderer.h :

#ifndef D3D10_RENDERER_H
#define D3D10_RENDERER_H

#include <windows.h>
#include <d3dx10math.h>
#include <DxErr.h>

#include "Defines.h"
#include "Singleton.h"
#include "Types.h"

#include "InputManager.h"

#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "d3dx10.lib")
#pragma comment(lib, "dxerr.lib")

class D3D10Renderer : public Singleton<D3D10Renderer>
{
public:
    D3D10Renderer(HWND hWnd);
    virtual ~D3D10Renderer();

    bool Initialize(bool bFullScreen);

    void Shutdown();

    void Render();

    ID3D10Device* GetDevice();

    uint32 GetViewportWidth();
    uint32 GetViewportHeight();
    
    void EnableZBuffer(bool bEnable);

    bool CreateSwapChain();
    bool CreateDepthStencilView();
    bool CreateRasterizerState();
    bool CreateDepthStencil();
    void CreateViewport();

    void UpdateWindowTitle();

    std::string Formater(const std::wstring& src);

    void SetFSAAMaximumLevel();

private:
    HWND m_hWnd;
    ID3D10Device* m_pd3dDevice;
    IDXGISwapChain* m_pSwapChain;        
    ID3D10RenderTargetView* m_pRenderTargetView;

    ID3D10Texture2D* m_pDepthStencil;
    ID3D10DepthStencilView* m_pDepthStencilView;
    ID3D10DepthStencilState* m_pDSState;
    ID3D10RasterizerState* m_pRasterizerState;

    uint32 m_iWidth;
    uint32 m_iHeight;

    bool m_bFullscreen;

    uint32 m_iFSAALevel;
};

#endif

 

Voici le code du fichier D3D10Renderer.cpp :

#include "D3D10Renderer.h"
#include "System.h"

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

template<> D3D10Renderer* Singleton<D3D10Renderer>::ms_instance = nullptr;

D3D10Renderer::D3D10Renderer(HWND hWnd) :
m_hWnd(hWnd),
m_pd3dDevice(nullptr),
m_pSwapChain(nullptr),
m_pRenderTargetView(nullptr),
m_pDepthStencil(nullptr),
m_pDSState(nullptr),
m_pDepthStencilView(nullptr),
m_iWidth(0),
m_iHeight(0),
m_bFullscreen(false),
m_iFSAALevel(8)
{
}

D3D10Renderer::~D3D10Renderer()
{
    if (m_pd3dDevice)
    {
        m_pd3dDevice->ClearState();
    }

    SAFE_RELEASE(m_pRenderTargetView);
    SAFE_RELEASE(m_pSwapChain);
    SAFE_RELEASE(m_pd3dDevice);
}

bool D3D10Renderer::Initialize(bool bFullscreen)
{
    m_bFullscreen = bFullscreen;

    // Variable pour stocker l'état erreur / succès
    HRESULT hr = S_OK;;

    if (!CreateSwapChain())
    {
        return false;
    }

    // Créé le back buffer
    ID3D10Texture2D* pBackBuffer;
    hr = m_pSwapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer);
    if (FAILED(hr))
    {
        return false;
    }

    // Créé la render target
    hr = m_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &m_pRenderTargetView);
    pBackBuffer->Release();
    if (FAILED(hr))
    {
        return false;
    }

    if (!CreateDepthStencilView())
    {
        return false;
    }

    if (!CreateRasterizerState())
    {
        return false;
    }

    if (!CreateDepthStencil())
    {
        return false;
    }

    CreateViewport();

    ShowCursor(false);

    return true;
}

void D3D10Renderer::Render()
{
    __int64 cntsPerSec = 0;
    QueryPerformanceFrequency((LARGE_INTEGER*)&cntsPerSec);

    __int64 currTimeStamp = 0;
    QueryPerformanceCounter((LARGE_INTEGER*)&currTimeStamp);

    static __int64 prevTimeStamp = currTimeStamp;

    float secsPerCnt = 1.0f / (float)cntsPerSec;   

    float fTimeSinceLastFrame = (currTimeStamp - prevTimeStamp) * secsPerCnt;

    static float afClearColor[4] = {0.85f, 1.0f, 1.0f, 1.0f}; 

    // Efface la surface de rendu
    m_pd3dDevice->ClearDepthStencilView(m_pDepthStencilView, D3D10_CLEAR_DEPTH, 1.0f, 0 );
    m_pd3dDevice->ClearRenderTargetView(m_pRenderTargetView, afClearColor);

    UpdateWindowTitle();

    m_pSwapChain->Present(0, 0);

    prevTimeStamp = currTimeStamp;
}

ID3D10Device* D3D10Renderer::GetDevice()
{
    return m_pd3dDevice;
}

uint32 D3D10Renderer::GetViewportWidth()
{
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

    m_pSwapChain->GetDesc(&swapChainDesc);

    return swapChainDesc.BufferDesc.Width;
}

uint32 D3D10Renderer::GetViewportHeight()
{
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

    m_pSwapChain->GetDesc(&swapChainDesc);

    return swapChainDesc.BufferDesc.Height;
}

void D3D10Renderer::EnableZBuffer(bool bEnable)
{
    D3D10_DEPTH_STENCIL_DESC pDesc;

    m_pDSState->GetDesc(&pDesc);
    pDesc.DepthEnable = bEnable;

    m_pd3dDevice->OMSetDepthStencilState(m_pDSState, 1);
}

void D3D10Renderer::CreateViewport()
{
    // Création du viewport
    D3D10_VIEWPORT vp;

    vp.Width = m_iWidth;
    vp.Height = m_iHeight;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;

    m_pd3dDevice->RSSetViewports( 1, &vp );
}

bool D3D10Renderer::CreateSwapChain()
{
    // Rectangle 2D pour les dimensions de la fenêtre
    RECT rc;

    // On obtient les dimensions de la fenêtre courante
    GetClientRect(m_hWnd, &rc);
    // Largeur de la fenêtre
    m_iWidth = rc.right - rc.left;
    // Hauteur de la fenêtre
    m_iHeight = rc.bottom - rc.top;

    // Paramètres de création du device
    UINT createDeviceFlags = 0;

    // Permet d'afficher les éventuelles erreurs de la création du device
    createDeviceFlags |= D3D10_CREATE_DEVICE_DEBUG;

    // Création de la Swap Chain
    // c'est-à-dire création du front buffer et du back buffer
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 1;
    // Taille de la surface en pixels
    sd.BufferDesc.Width = m_iWidth;
    sd.BufferDesc.Height = m_iHeight;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    // Format des couleurs
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 0;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    // L'handle de la fenêtre que l'on va afficher dessus
    sd.OutputWindow = m_hWnd;
    sd.SampleDesc.Count = m_iFSAALevel;
    sd.SampleDesc.Quality = 0;
    // Fenêtré ou non
    sd.Windowed = !m_bFullscreen;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    // On créé le device DirectX 10 et la Swap Chain
    HRESULT hr = D3D10CreateDeviceAndSwapChain(nullptr, D3D10_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags,
                                               D3D10_SDK_VERSION, &sd, &m_pSwapChain, &m_pd3dDevice);
    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

    SetFSAAMaximumLevel();

    return true;
}

void D3D10Renderer::SetFSAAMaximumLevel()
{
    HRESULT hr = S_OK;
    UINT maxQualityLevel = 1;
    for (uint32 iSampleCount = 1; iSampleCount <= D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT; iSampleCount++)
    {
        hr = m_pd3dDevice->CheckMultisampleQualityLevels(
            DXGI_FORMAT_R8G8B8A8_UNORM, iSampleCount, &maxQualityLevel);

        if (hr != S_OK)
        {
            fastprint(Formater(L"CheckMultisampleQualityLevels a échoué."));
        }
                                 
        if (maxQualityLevel > 0)
        {
            fastprint("MSAA " << iSampleCount << Formater(L"X supportée par la carte vidéo avec ") <<
                maxQualityLevel << Formater(L" niveau(x) de qualité."));
        }
    }
}

bool D3D10Renderer::CreateDepthStencilView()
{
    HRESULT hr = S_OK;

    D3D10_TEXTURE2D_DESC descDepth;
    ZeroMemory(&descDepth, sizeof(descDepth));
    descDepth.Width = m_iWidth;
    descDepth.Height = m_iHeight;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D32_FLOAT;
    descDepth.SampleDesc.Count = m_iFSAALevel;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D10_USAGE_DEFAULT;
    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;

    hr = m_pd3dDevice->CreateTexture2D(&descDepth, nullptr, &m_pDepthStencil);

    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }
        
    D3D10_DEPTH_STENCIL_VIEW_DESC descDSV;
    descDSV.Format = descDepth.Format;

    if (m_iFSAALevel > 1)
    {
        descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2DMS;
    }
    else
    {
        descDSV.ViewDimension = D3D10_DSV_DIMENSION_TEXTURE2D;
    }

    descDSV.Texture2D.MipSlice = 0;

    hr = m_pd3dDevice->CreateDepthStencilView(m_pDepthStencil, &descDSV, &m_pDepthStencilView);

    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

    m_pd3dDevice->OMSetRenderTargets(1, &m_pRenderTargetView, m_pDepthStencilView);

    return true;
}

bool D3D10Renderer::CreateRasterizerState()
{
    D3D10_RASTERIZER_DESC rasterizerState;

    rasterizerState.CullMode = D3D10_CULL_NONE;
    rasterizerState.FillMode = D3D10_FILL_SOLID;
    rasterizerState.FrontCounterClockwise = true;
    rasterizerState.DepthBias = false;
    rasterizerState.DepthBiasClamp = 0;
    rasterizerState.SlopeScaledDepthBias = 0;
    rasterizerState.DepthClipEnable = true;
    rasterizerState.ScissorEnable = false;
    rasterizerState.MultisampleEnable = true;
    rasterizerState.AntialiasedLineEnable = true;

    HRESULT hr = m_pd3dDevice->CreateRasterizerState(&rasterizerState, &m_pRasterizerState);    

    m_pd3dDevice->RSSetState(m_pRasterizerState);

    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

    return true;
}

bool D3D10Renderer::CreateDepthStencil()
{
    D3D10_DEPTH_STENCIL_DESC pDesc;

    pDesc.DepthEnable = true;
    pDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
    pDesc.DepthFunc = D3D10_COMPARISON_LESS;
    pDesc.StencilEnable = true;
    pDesc.StencilReadMask = 0xFF;
    pDesc.StencilWriteMask = 0xFF;
    pDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
    pDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR;
    pDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
    pDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
    pDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP;
    pDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR;
    pDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
    pDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS;

    HRESULT hr = m_pd3dDevice->CreateDepthStencilState(&pDesc, &m_pDSState);

    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

    m_pd3dDevice->OMSetDepthStencilState(m_pDSState, 1);

    return true;
}

FPSCamera* D3D10Renderer::GetCamera()
{
    return m_pCamera;
}

void D3D10Renderer::OnKeyPressed(const KeyEvent& arg)
{
    if (arg.keyCode == VK_ESCAPE)
    {
        SYSTEM->Quit();
    }
}

void D3D10Renderer::OnKeyReleased(const KeyEvent& arg)
{
}

void D3D10Renderer::UpdateWindowTitle()
{
    static char buffer[256];
    D3DXVECTOR3 camPos = D3DXVECTOR3(1.0f, 1.0f, 1.0f);

    sprintf(buffer, "Position de la caméra : X = %.2f Y = %.2f Z = %.2f", camPos.x, camPos.y, camPos.z);
 
    // On met à jour le titre de la fenêtre
    SetWindowTextA(m_hWnd, buffer);
}

std::string D3D10Renderer::Formater(const std::wstring& src)
{
    char outString[512];
    CharToOemW(src.c_str(), outString);
 
    return std::string(outString);
}

 

Résumé :

Nous avons présenté les deux fichiers nécessaire à l’initialiser du rendu graphique de DirectX !

Kit basique d’initialisation DirectX 10.1

 

Kit

KIT_2

Intro :

Voici un kit formé d’un ensemble de fichiers qui pourra servir de base pour vos programmes graphiques ou essais 3D. Le kit comprend à peu près 4000 lignes de code.

Prérequis :

– Savoir lire du C++

Explications :

[rajouter : ]

[fullscreen switch]

[shadermanager]

Il comprend :

– le support de la transparence

– une caméra classique

– une caméra de type FPS (First Person Shooter) avec laquelle on peut voler partout

– une énumération de couleurs

– un gestionnaire D3D10Renderer quasi complet

– la possibilité de lancer l’application en plein-écran ou non

– la possibilité d’afficher du texte

– des gestionnaires en design-pattern Singleton (classe Singleton<class>)

– un ensemble de macros toutes utiles

– un gestionnaire InputManager pour gérer les entrées clavier et souris de l’utilisateur

– une classe SceneNode pour représenter vos entités sur la scène

– avec fonctions de translation, rotation et agrandissement

– un gestionnaire de fichier effet Shader avec gestion automatique des variables shader

– quelques définitions de shaders (ex : simple, lumière)

– une classe SkyDomeSceneNode (pour afficher un ciel avec une TextureCube)

– une classe SphereMeshSceneNode pour générer une sphère de façon procédurale

– une classe System

– une classe TextureManager pour charger les fichiers textures rapidement

– une liste complète de déclaration de vertex (avec les Vertex Input Layout)

– une classe Utils avec fonctions toutes très utiles

Résumé :

Voici l’archive contenant le kit et tous ses fichiers associés.

Références :

– stackoverflow.com

Obtenir les informations de votre CG avec les interfaces DXGI*

gtx

Intro :

Il peut être utile d’obtenir le nom de la carte vidéo, sa mémoire vive maximum et tous ses modes d’affichage possibles pour un jeu vidéo.

Prérequis :

– Savoir initialiser DirectX 10

Explications :

Avant tout, voici deux petites macros affichant les erreurs tout en retournant false s’il y a en une !

#define ShowMessageBoxDXError(hr) char buf[2048]; \
       sprintf(buf, "Error : %s\nDescription : %s\n\nFile : %s\n\nLine : %d\n\nFunction : %s", DXGetErrorStringA(hr), DXGetErrorDescriptionA(hr), __FILE__, __LINE__, __FUNCTION__); \
       MessageBoxA(nullptr, buf, "Erreur", MB_ICONHAND | MB_OK);

#if defined(DEBUG) | defined(_DEBUG)
    #ifndef HR_FAILED_RETURN_FALSE
    #define HR_FAILED_RETURN_FALSE(x)                  \
    {                                                  \
        if (FAILED(x))                                \
        {                                              \
            ShowMessageBoxDXError(x)                  \
            return false;                              \
        }                                              \
    }
    #endif
 
#else
    #ifndef HR_FAILED_RETURN_FALSE
    #define HR_FAILED_RETURN_FALSE(x) x;
    #endif
#endif

 

Voici la fonction qui affiche les informations sur la carte graphique :


bool GetGPUInfo()
{
    HRESULT hr = S_OK;

    uint32 iNumModes;

    IDXGIFactory* pDXGIFactory = nullptr;
    IDXGIAdapter* pDXGIAdapter = nullptr;
    IDXGIOutput* pDXGIAdapterOutput = nullptr;

    DXGI_MODE_DESC* pDisplayModeList = nullptr;

    DXGI_ADAPTER_DESC adapterDesc;

    // Créée une interface métier "factory" DirectX
    hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)&pDXGIFactory);

    HR_FAILED_RETURN_FALSE(hr);

    // Utilise la "factory"
    hr = pDXGIFactory->EnumAdapters(0, &pDXGIAdapter);

    HR_FAILED_RETURN_FALSE(hr);

    // Enumère l'adpatateur principal de l'écran
    hr = pDXGIAdapter->EnumOutputs(0, &pDXGIAdapterOutput);

    HR_FAILED_RETURN_FALSE(hr);

    // Obtient le nombre de modes d'affichage qui corresppond à DXGI_FORMAT_R8G8B8A8_UNORM  
    hr = pDXGIAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &iNumModes, nullptr);

    HR_FAILED_RETURN_FALSE(hr);

    /* Créé une liste qui détient tous les modes d'affichage possibles pour cette carte graphique et
       le format d'affichage */
    pDisplayModeList = new DXGI_MODE_DESC[iNumModes];

    if (!pDisplayModeList)
    {
        return false;
    }

    // Remplie la précédente structure
    hr = pDXGIAdapterOutput->GetDisplayModeList(DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_ENUM_MODES_INTERLACED, &iNumModes, pDisplayModeList);
    
    HR_FAILED_RETURN_FALSE(hr);

    /* On parcourt tous les modes d'affichages possibles
       et on trouve celui qui correspond à la taille spécifiée
       en paramètre ; puis on enregistre le numérateur et le dénominateur
       du taux de rafraichissement de l'écran
    */
    std::cout << "Modes d'affichage pour le format : DXGI_FORMAT_R8G8B8A8_UNORM" << std::endl << std::endl;

    for (uint32 i = 0; i < iNumModes; i++)
    {
        DXGI_MODE_DESC desc = pDisplayModeList[i];
        
        std::string sRefreshRate = std::to_string(desc.RefreshRate.Numerator / desc.RefreshRate.Denominator) + "Hz";

        std::cout << desc.Width << "x" << desc.Height << " " << sRefreshRate << std::endl;
    }

    std::cout << std::endl;

    hr = pDXGIAdapter->GetDesc(&adapterDesc);

    HR_FAILED_RETURN_FALSE(hr);

    // On enregistre la taille de la mémoire vidéo en mégabytes
    m_iVideoCardMemory = (adapterDesc.DedicatedVideoMemory / 1024 / 1024);

    size_t iStringLength = 0;
    // Convertie le nom la carte vidéo dans une chaîne de caractères
    int iError = wcstombs_s(&iStringLength, m_sVideoCardDescription, 128, adapterDesc.Description, 128);

    if (iError != 0)
    {
        return false;
    }

    std::cout << std::endl;

    std::cout << m_sVideoCardDescription << std::endl;

    std::cout << "Video memory = " << std::to_string(m_iVideoCardMemory) << " MB" << std::endl;

    std::cout << std::endl;

    SAFE_DELETE_ARRAY(pDisplayModeList);

    SAFE_RELEASE(pDXGIAdapterOutput);
    SAFE_RELEASE(pDXGIAdapter);
    SAFE_RELEASE(pDXGIFactory);

    return true;
}

 

Voici la fonction qui affiche les capacités de la carte graphique en matière d’anti-aliasing (déjà présentée dans les articles précédents) :


void PrintFSAAMaximumLevel()
{
    HRESULT hr = S_OK;
    UINT maxQualityLevel = 1;
    
    for (uint32 iSampleCount = 1; iSampleCount <= D3D10_MAX_MULTISAMPLE_SAMPLE_COUNT; iSampleCount++)
    {
        hr = m_pd3dDevice->CheckMultisampleQualityLevels(
            DXGI_FORMAT_R8G8B8A8_UNORM, iSampleCount, &maxQualityLevel);

        if (hr != S_OK)
        {
            std::cout << "CheckMultisampleQualityLevels a échoue." << std::endl;
        }
                                 
        if (maxQualityLevel > 0)
        {
            std::cout << "MSAA " << iSampleCount << "X supportee par la carte video avec " <<
                maxQualityLevel << " niveau(x) de qualite." << std::endl;
        }
    }
}

 

Voici la sortie de la console une fois affichés les modes d’affichage :

CG_specs

 

Résumé :

Nous avons appris comment obtenir les informations à partir de la carte graphique.

Les principaux objets utilisés pour initialiser DirectX 11

objects

Intro :

Il est nécessaire de manipuler une bonne dizaine d’objets ou d’interfaces pour initialiser DirectX 11 avant de commencer tout affichage 3D.

Prérequis :

– Avoir déjà lu le code pour initialiser DirectX 10 ou 11

Explications :

Les-voici énumérés ci-dessous.

/* Objets nécessaires pour afficher du rendu 3D à l'écran */
IDXGISwapChain* m_pSwapChain = nullptr;
ID3D11Device* m_pDevice = nullptr;
ID3D11DeviceContext* m_pDeviceContext = nullptr;
ID3D11RenderTargetView* m_pRenderTargetView = nullptr;
ID3D11Texture2D* m_pDepthStencilBuffer = nullptr;
ID3D11DepthStencilState* m_pDepthStencilState = nullptr;
ID3D11DepthStencilView* m_pDepthStencilView = nullptr;
ID3D11RasterizerState* m_pRasterState = nullptr;

/* Objets pour effectuer des requêtes à la carte graphique */
IDXGIFactory* pDXGIFactory = nullptr;
IDXGIAdapter* pDXGIAdapter = nullptr;
IDXGIOutput* pDXGIAdapterOutput = nullptr;

ID3D11Texture2D* pBackBuffer = nullptr;

/* Objets de description de données pour les objets nécessaires au rendu */
DXGI_SWAP_CHAIN_DESC swapChainDesc;
D3D11_TEXTURE2D_DESC depthBufferDesc;
D3D11_DEPTH_STENCIL_DESC depthStencilDesc;
D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
D3D11_RASTERIZER_DESC rasterDesc;
DXGI_ADAPTER_DESC adapterDesc;
DXGI_MODE_DESC DisplayModeList;
D3D11_VIEWPORT viewport;

 


 

Afficher du rendu 3D :

IDXGISwapChain : interface qui stocke une ou plusieurs surfaces antérieures de rendu avant d’effectuer l’affichage à l’écran.

Voici ses méthodes principales :

/* Accède à une surface de back buffer */
HRESULT GetBuffer(
  [in]       UINT Buffer,
  [in]       REFIID riid,
  [in, out]  void **ppSurface
);

/* Obtient les données descriptives de la swap chain */
HRESULT GetDesc(
  [out]  DXGI_SWAP_CHAIN_DESC *pDesc
);

/* Affiche une image à l'écran de l'utilisateur */
HRESULT Present(
  [in]  UINT SyncInterval,
  [in]  UINT Flags
);

 

ID3D11Device : interface qui représente un afficheur vidéo. Elle est utilisée soit pour afficher du rendu 3D soit pour créer des ressources.

Voici ses méthodes principales :

/* Créé un buffer quelconque qui peut être un vertex buffer,
   un index buffer ou un shader-constant buffer */
HRESULT CreateBuffer(
  [in]   const D3D11_BUFFER_DESC *pDesc,
  [in]   const D3D11_SUBRESOURCE_DATA *pInitialData,
  [out]  ID3D11Buffer **ppBuffer
);

/* Créé une "depth stencil view" pour accéder aux
   données des ressources */
HRESULT CreateDepthStencilView(
  [in]   ID3D11Resource *pResource,
  [in]   const D3D11_DEPTH_STENCIL_VIEW_DESC *pDesc,
  [out]  ID3D11DepthStencilView **ppDepthStencilView
);

/* Créé un objet "input-layout" qui servira à l'étape de rendu Input Assembler */
HRESULT CreateInputLayout(
  [in]   const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,
  [in]   UINT NumElements,
  [in]   const void *pShaderBytecodeWithInputSignature,
  [in]   SIZE_T BytecodeLength,
  [out]  ID3D11InputLayout **ppInputLayout
);

/* Créé un pixel shader */
HRESULT CreatePixelShader(
  [in]   const void *pShaderBytecode,
  [in]   SIZE_T BytecodeLength,
  [in]   ID3D11ClassLinkage *pClassLinkage,
  [out]  ID3D11PixelShader **ppPixelShader
);

/* Créé un vertex shader */
HRESULT CreateVertexShader(
  [in]   const void *pShaderBytecode,
  [in]   SIZE_T BytecodeLength,
  [in]   ID3D11ClassLinkage *pClassLinkage,
  [out]  ID3D11VertexShader **ppVertexShader
);

/* Créé les données descriptives sur comment doit réagir l'étape
   de rendu Razterizer */
HRESULT CreateRasterizerState(
  [in]   const D3D11_RASTERIZER_DESC *pRasterizerDesc,
  [out]  ID3D11RasterizerState **ppRasterizerState
);

/* Créé une render-target */
HRESULT CreateRenderTargetView(
  [in]   ID3D11Resource *pResource,
  [in]   const D3D11_RENDER_TARGET_VIEW_DESC *pDesc,
  [out]  ID3D11RenderTargetView **ppRTView
);

/* Créé un état depth-stencil  */
HRESULT CreateDepthStencilState(
  [in]   const D3D11_DEPTH_STENCIL_DESC *pDepthStencilDesc,
  [out]  ID3D11DepthStencilState **ppDepthStencilState
);

/* Créé une ressource shader */
HRESULT CreateShaderResourceView(
  [in]   ID3D11Resource *pResource,
  [in]   const D3D11_SHADER_RESOURCE_VIEW_DESC *pDesc,
  [out]  ID3D11ShaderResourceView **ppSRView
);

/* Créé une texture 2D */
HRESULT CreateTexture2D(
  [in]   const D3D11_TEXTURE2D_DESC *pDesc,
  [in]   const D3D11_SUBRESOURCE_DATA *pInitialData,
  [out]  ID3D11Texture2D **ppTexture2D
);

 

ID3D11DeviceContext : une interface qui représente un contexte d’affichage (device context) générant des commandes de rendu.

Voici ses méthodes principales :


/* Marque le début d'une série de commandes graphiques */
void Begin(
    ID3D11Asynchronous *pAsync
);

/* Efface le depth stencil */
void ClearDepthStencilView(
  [in]  ID3D11DepthStencilView *pDepthStencilView,
  [in]  UINT ClearFlags,
  [in]  FLOAT Depth,
  [in]  UINT8 Stencil
);

/* Reinitialise tous les élements de la render target à 1 */
void ClearRenderTargetView(
  [in]  ID3D11RenderTargetView *pRenderTargetView,
  [in]  const FLOAT ColorRGBA
);

/* Affiche des primitives non indexées */
void Draw(
  [in]  UINT VertexCount,
  [in]  UINT StartVertexLocation
);

/* Affiche des primitives indexées */
void DrawIndexed(
  [in]  UINT IndexCount,
  [in]  UINT StartIndexLocation,
  [in]  INT BaseVertexLocation
);

/* Relie un index buffer à l'étape de rendu Input Assembler */
void IASetIndexBuffer(
  [in]  ID3D11Buffer *pIndexBuffer,
  [in]  DXGI_FORMAT Format,
  [in]  UINT Offset
);

/* Relie un Vertex Input Layout à l'étape de rendu Input Assembler */
void IASetInputLayout(
  [in]  ID3D11InputLayout *pInputLayout
);

/* Relie le type de primitive à l'étape de rendu Input Assembler */
void IASetPrimitiveTopology(
  [in]  D3D11_PRIMITIVE_TOPOLOGY Topology
);

/* Obtient un pointeur sur la ressource dans la mémoire du GPU,
   ceci empeche la CG d'accéder à la ressource */
HRESULT Map(
    ID3D11Resource *pResource,
    UINT Subresource,
    D3D11_MAP MapType,
    UINT MapFlags,
    D3D11_MAPPED_SUBRESOURCE *pMappedResource
);

/* Invalide le pointeur de la ressource dans la mémoire du GPU,
   redonne l'accès à la ressource par la CG */
void Unmap(
    ID3D11Resource *pResource,
    UINT Subresource
);

/* Configure l'état du depth-stencil à l'étape de rendu Output Merger*/
void OMSetDepthStencilState(
  [in]  ID3D11DepthStencilState *pDepthStencilState,
  [in]  UINT StencilRef
);

/* Relie une ou plusieurs render target automatiquement et
   en même temps le buffer depth-stencil à l'étape de rendu Output Merger */
void OMSetRenderTargets(
  [in]  UINT NumViews,
  [in]  ID3D11RenderTargetView *const **ppRenderTargetViews,
  [in]  ID3D11DepthStencilView *pDepthStencilView
);

/* Configure l'étape de rendu Rasterizer */
void RSSetState(
  [in]  ID3D11RasterizerState *pRasterizerState
);

/* Relie un tablea de viewport à l'étape de rendu Rasterizer */
void RSSetViewports(
  [in]  UINT NumViewports,
  [in]  const D3D11_VIEWPORT *pViewports
);

 

ID3D11RenderTargetView : une interface qui représente une render-target (voir lexique) qui peut être accédée lors du rendu.

Elle possède la méthode principale :

GetDesc(D3D11_RENDER_TARGET_VIEW_DESC *pDesc)

 

ID3D11Texture2D : une interface qui représente une texture constituée de données texel qui
sont stockées dans la mémoire.

Elle possède la méthode principale :

GetDesc(D3D11_TEXTURE2D_DESC *pDesc)

 

ID3D11DepthStencilState : une interface qui représente l’état du depth-stencil pour que celui-ci
opère des tests à l’étape output-merger.

Elle possède la méthode principale :

GetDesc(D3D11_DEPTH_STENCIL_DESC *pDesc)

 

ID3D11DepthStencilView : une interface qui peut accéder à une texture paramétrée durant le test du depth-stencil.

Elle possède la méthode principale :

GetDesc(D3D11_DEPTH_STENCIL_VIEW_DESC *pDesc)

 

Cette structure nommée D3D11_DEPTH_STENCIL_VIEW_DESC est décrite ci-dessous :

struct D3D11_DEPTH_STENCIL_VIEW_DESC {
  DXGI_FORMAT         Format;
  D3D11_DSV_DIMENSION ViewDimension;
  UINT                Flags;
  union {
    D3D11_TEX1D_DSV         Texture1D;
    D3D11_TEX1D_ARRAY_DSV   Texture1DArray;
    D3D11_TEX2D_DSV         Texture2D;
    D3D11_TEX2D_ARRAY_DSV   Texture2DArray;
    D3D11_TEX2DMS_DSV       Texture2DMS;
    D3D11_TEX2DMS_ARRAY_DSV Texture2DMSArray;
  } ;
}

 

ID3D11RasterizerState : une interface qui permet de configurer l’état du Rasterzizer pour l’étape de rendu Rasterzizer.

Elle possède la méthode principale GetDesc(D3D11_RASTERIZER_DESC *pDesc).
Cette structure nommée D3D11_RASTERIZER_DESC est décrite ci-dessous :

struct D3D11_RASTERIZER_DESC {
  D3D11_FILL_MODE FillMode;
  D3D11_CULL_MODE CullMode;
  BOOL            FrontCounterClockwise;
  INT             DepthBias;
  FLOAT           DepthBiasClamp;
  FLOAT           SlopeScaledDepthBias;
  BOOL            DepthClipEnable;
  BOOL            ScissorEnable;
  BOOL            MultisampleEnable;
  BOOL            AntialiasedLineEnable;
}

 

Objets de description de données :

Une structure nommée DXGI_SWAP_CHAIN_DESC est décrite ci-dessous :

typedef struct DXGI_SWAP_CHAIN_DESC {
  DXGI_MODE_DESC   BufferDesc; // Décrit le mode d'affiche du backbuffer
  DXGI_SAMPLE_DESC SampleDesc; // Spécifie le super-échantillonnage
  DXGI_USAGE       BufferUsage; // Décrit le mode d'utilisation du backbuffer
                                // par le CPU
  UINT             BufferCount; // Spécifie le nombre de surface de tampon
  HWND             OutputWindow; // Spécifie l'handle de la fenêtre
                                 // Windows à utiliser
  BOOL             Windowed;     // Mode fenêtré ou non
  DXGI_SWAP_EFFECT SwapEffect;   // Spécifie la façon dont le contenu est affiché
  UINT             Flags;      
} DXGI_SWAP_CHAIN_DESC;

 

Une structure nommée D3D11_TEXTURE2D_DESC est décrite ci-dessous :

typedef struct D3D11_TEXTURE2D_DESC {
  UINT             Width;
  UINT             Height;
  UINT             MipLevels;
  UINT             ArraySize;
  DXGI_FORMAT      Format;
  DXGI_SAMPLE_DESC SampleDesc;
  D3D11_USAGE      Usage;
  UINT             BindFlags;
  UINT             CPUAccessFlags;
  UINT             MiscFlags;
} D3D11_TEXTURE2D_DESC

 

Une structure nommée D3D11_VIEWPORT est décrite ci-dessous :

typedef struct D3D11_VIEWPORT {
  FLOAT TopLeftX;
  FLOAT TopLeftY;
  FLOAT Width;
  FLOAT Height;
  FLOAT MinDepth;
  FLOAT MaxDepth;
} D3D11_VIEWPORT;

 

Résumé :

L’initialisation de DirectX 11 est assez délicate à mettre en œuvre car elle nécessite la manipulation de nombreux d’objets et interfaces associés.

Références :

– Documentation – DirectX SDK – June 2010

La tessellation – Application (DirectX 11)

tessellation

Intro :

Nous allons voir comment mettre en pratique la « tessellation » de DirectX 11.

Prérequis :

– Avoir suivi la première partie de ce tutoriel concernant la tessellation.

Explications :

Le principe est simple : on ajoute de nouvelles vertices à un modèle puis on déplace ces vertices en suivant les données correspondantes d’une « height map». Il s’agit en l’occurrence du procédé de « Displacement Mapping ».

Résumé :

Références :

Les principales étapes de rendu de DirectX 11

Intro :

Les étapes de la chaîne de rendu de DirectX 11 sont les mêmes que celles de DirectX 10.1, sauf qu’il y a été ajouté le support des nouvelles fonctionnalités de tessellation.

Prérequis :

– Connaître les étapes de la chaîne de rendu de DirectX 10.1

Explications :

Voici un schéma qui les résume :

pipeline

Seuls les étapes Hull Shader et Domain Shader sont programmables ; l’étape Tessellator est rigidement fixée par le GPU.

Étape Hull Shader :

 

Étape Tessellator Shader :

Une étape de la chaîne du rendu graphique non-programmable qui s’occupe de subdiviser un « domain » (un une face, un triangle, une ligne) en morceaux plus petits.

Étape Domain Shader :

Un programme shader qui calcule la position du vertex d’un point subdivisé par « l’output patch ».

Résumé :

Nous avons présenté les nouvelles étapes de la « pipeline » du rendu graphique de DirectX 11 concernant la tessellation.

Références :

– DirectX SDK 2010

La tessellation – Théorie (DirectX 11)

tessellation

Intro :

Le principe de la tessellation est simple : on transforme un polygone et on le décompose en morceaux plus petits.

Prérequis :

– Savoir comment fonctionne un programme HLSL

– Savoir comment utiliser DirectX 11

Explications :

Si on prend un carré et qu’on le coupe en deux le long de sa diagonale, on opère une « tessellation » en deux triangles.

Dans DirectX 11, la tessellation est opérée directement par le GPU (c’est-à-dire par le processeur de la carte graphique).

Elle peut être utilisée dans le rendu en faisant du « Displacement Mapping ». Au lieu de précharger, dans la mémoire vidéo, un modèle très détaillé : on peut augmenter ses détails par le GPU ; en effet le Displacement Mapping va de paire avec la tessellation.

Par exemple au lieu d’implémenter une grille plate très détaillée, à la place, on peut augmenter les détails d’un polygone carré par le procédé de tessellation.

En conséquence et d’autre part, il devient possible de faire varier progressivement le niveau de détail géométrique des modèles.

Les avantages :

– Réduit la mémoire et la bande passante utilisées

– Permet de complexifier des modèles basse résolution en des modèles très détaillés et ceci à la volée

– Déplace le calcul classique de modification géométrique effectué par le CPU vers un calcul effectué par le GPU

– Facilite le LOD (Level of Detail) des objets distants. En effet la tessellation permet de faire varier dynamiquement le niveau de détail d’un modèle

– On peut effectuer le calcul physique de collision ou d’animation avec un modèle non détaillé en mémoire centrale tout en affichant un modèle très détaillé au rendu

– Possibilité d’affichage de meilleurs modèles détaillés

Trois nouvelles étapes de shader :

dx11_pipeline

DirectX 11 implémente la tessellation par 3 nouvelles étapes de chaîne de rendu : l’étape Hull Shader, l’étape Tessellator et l’étape Domain.

Ces trois étapes suive l’étape Vertex Shader et précède l’étape Geometry Shader.

Avant tout, définissons quelques concepts liés à la tessellation par DirectX 11 :

Patch :
Control Point :
Tessellation factor :
Input primitive :

Lorsque ces deux étapes (Hull Shader & Domain Shader) sont définis dans un fichier shader « .fx », l’étape Vertex Shader ne renvoie pas spécialement un vertex mais renvoie ce qu’on appel un « control point » qui représente le vertex d’un triangle ou d’une face. Ce sera la dernière étape de la « Pipeline » qui se chargera de renvoyer le vertex au Pixel Shader.

Hull Shader :

Est invoqué une fois par « control point » et renvoie des « tessellator factors ». Cette étape est programmable. Cette étape de shader permet de faire savoir à l’étape suivante Tesselator comment opérer la tesselation sur les données d’entrées.

TesselatorLevel

Tessellator Stage :

Génère de nouvelles vertices. Plus les « tessellator factors » reçues sont élevés plus il y a de triangles créés. Cette étape est non-programmable.

Domain Shader :

Cette étape est programmable. Est exécutée une fois par vertex. Convertis et fait correspondre les données (u, v) vers les données d’attributs (x, y, z, w).

Exemples :

1) Un carré subissant une « tessellation »

tesselator

 2) Puis utilisation d’une « displacement map »

tesselator_2

3) Enfin utilisation d’une texture et bump mapping

Tessellation3

Résumé :

Références :

– http://www.nvidia.fr/object/tessellation_fr.html

– https://msdn.microsoft.com/en-us/library/windows/desktop/ff476340%28v=vs.85%29.aspx

– http://www.rastertek.com/dx11tut38.html

– http://richardssoftware.net/Home/Post/28

– 3D Game Programming with DirectX 11 – Franck D. Luna

Une classe AlphaPass pour activer la transparence (façon RAII)

alpha

Intro :

Parfois on veut afficher certaines modèles tout en activant la transparence.

Ici je présente une façon simple de gérer cela avec le principe du RAII.

Prérequis :

– Comprendre le principe du RAII

– Savoir initialiser DirectX 10 à travers la classe D3D10Renderer

Explications :

Voici le code du fichier AlphaPass.h :

#ifndef ALPHA_PASS_H
#define ALPHA_PASS_H

#include <d3dx10math.h>

class AlphaPass
{
public:
    AlphaPass();
    virtual ~AlphaPass();

protected:
    ID3D10BlendState* m_pOldBlendState;
    float m_OldBlendFactor[4];
    unsigned int m_OldSampleMask;
        
    ID3D10BlendState* m_pCurrentBlendState;
};

#endif

 

Voici le fichier AlphaPass.cpp :

#include "AlphaPass.h"

AlphaPass::AlphaPass() :
m_pOldBlendState(nullptr),
m_pCurrentBlendState(nullptr)
{
    D3D10_RENDERER->GetDevice()->OMGetBlendState(&m_pOldBlendState, m_OldBlendFactor, &m_OldSampleMask);
    m_pCurrentBlendState = nullptr;

    D3D10_BLEND_DESC BlendState;
    ZeroMemory(&BlendState, sizeof(D3D10_BLEND_DESC));

    BlendState.BlendEnable[0] = TRUE;
    BlendState.SrcBlend = D3D10_BLEND_SRC_ALPHA;
    BlendState.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
    BlendState.BlendOp = D3D10_BLEND_OP_ADD;
    BlendState.SrcBlendAlpha = D3D10_BLEND_ZERO;
    BlendState.DestBlendAlpha = D3D10_BLEND_ZERO;
    BlendState.BlendOpAlpha = D3D10_BLEND_OP_ADD;
    BlendState.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;

    D3D10_RENDERER->GetDevice()->CreateBlendState(&BlendState, &m_pCurrentBlendState);
    D3D10_RENDERER->GetDevice()->OMSetBlendState(m_pCurrentBlendState, 0, 0xffffffff);
}

AlphaPass::~AlphaPass()
{
    D3D10_RENDERER->GetDevice()->OMSetBlendState(m_pOldBlendState, m_OldBlendFactor, m_OldSampleMask);

    SAFE_RELEASE(m_pCurrentBlendState);
    SAFE_RELEASE(m_pOldBlendState);
}

 

warning

Vos entités transparentes doivent être affichées en dernier lors de le boucle de rendu et tout en désactivant le Z-Buffer.

Résumé :

Nous avons établi un objet AlphaPass qui permet d’activer, lors de sa création sur la pile, la transparence dans le rendu. La transparence est restauré lors de la destruction de l’objet en sortant du bloc de code.

Une classe Renderable pour afficher vos contenus 3D

test_remeshing

Intro :

Pour afficher rapidement du contenu 3D, il est laborieux de déclarer à chaque fois les vertex et index buffers, le shader associé, le Vertex Input Layout, la méthode d’affichage, etc…

Cette classe suivante vous permettra de restreindre ces difficultés.

Prérequis :

– Savoir utiliser la classe ShaderTechnique. Voir cet article.

– Savoir un peu utiliser DirectX 10.

Explications :

Voici le fichier Renderable.h :

#ifndef RENDERABLE_H
#define RENDERABLE_H

class ShaderTechnique;

class Renderable
{
public:
    enum DrawMethod
    {
        DRAW_NORMAL,
        DRAW_INDEXED
    };

    Renderable();
    virtual ~Renderable();

    virtual void Render(float fTimeSinceLastFrame);

    void SetShaderTechnique(ShaderTechnique* pShader);
    ShaderTechnique* GetShaderTechnique();

    void SetVertexType(VertexLayoutType vertexType);
    void SetDrawMethod(DrawMethod drawMethod);
    void SetTopology(D3D_PRIMITIVE_TOPOLOGY topology);

    void SetRenderableName(const std::string& sName);
    std::string GetRenderableName();

protected:
    void SetVertexBuffer(ID3D10Buffer* pBuffer);
    void SetIndexBuffer(ID3D10Buffer* pBuffer);

    ID3D10Buffer* GetVertexBuffer();
    ID3D10Buffer* GetIndexBuffer();

protected:
    unsigned int m_iVerticesCount;
    unsigned int m_iIndicesCount;

    ID3D10Buffer* m_pVertexBuffer;
    ID3D10Buffer* m_pIndexBuffer;

private:
    void Draw();
    void InitVertexBuffer();
    void InitIndexBuffer();

private:
    VertexLayoutType m_vertexType;
    DrawMethod m_drawMethod;
    D3D_PRIMITIVE_TOPOLOGY m_topology;

    ShaderTechnique* m_pShader;

    std::string m_sName;
};

#endif

 

Voici le fichier Renderale.cpp :

#include "Renderable.h"

Renderable::Renderable() :
m_pShader(nullptr),
m_vertexType(VertexLayoutType::PTN_VERTEX),
m_drawMethod(DrawMethod::DRAW_NORMAL),
m_topology(D3D_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST),
m_iIndicesCount(0),
m_iVerticesCount(0),
m_pVertexBuffer(nullptr),
m_pIndexBuffer(nullptr)
{
}

Renderable::~Renderable()
{
    SAFE_RELEASE(m_pIndexBuffer);
    SAFE_RELEASE(m_pVertexBuffer);
}

void Renderable::SetShaderTechnique(ShaderTechnique* pShader)
{
    Assert(pShader);

    bool bSucess = pShader->Initialize();

    if (bSucess)
    {
        m_pShader = pShader;
    }
}

ShaderTechnique* Renderable::GetShaderTechnique()
{
    return m_pShader;
}

void Renderable::Render(float fTimeSinceLastFrame)
{
    if (m_pShader == nullptr)
    {
        return;
    }

    D3D10_RENDERER->GetDevice()->IASetInputLayout(m_pShader->GetVertexLayout());
    D3D10_RENDERER->GetDevice()->IASetPrimitiveTopology(m_topology);

    InitIndexBuffer();
    InitVertexBuffer();

    m_pShader->Update(fTimeSinceLastFrame);

    D3D10_TECHNIQUE_DESC techDesc;
    ID3D10EffectTechnique* pTechnique = m_pShader->GetShaderTechnique();

    pTechnique->GetDesc(&techDesc);

    for (unsigned int p = 0; p < techDesc.Passes; p++)
    {
        pTechnique->GetPassByIndex(p)->Apply(0);
        Draw();
    }
}

void Renderable::Draw()
{
    if (m_drawMethod == DrawMethod::DRAW_NORMAL)
    {
        D3D10_RENDERER->GetDevice()->Draw(m_iVerticesCount, 0);
    }
    else if (m_drawMethod == DrawMethod::DRAW_INDEXED)
    {
        D3D10_RENDERER->GetDevice()->DrawIndexed(m_iIndicesCount, 0, 0);
    }
}

void Renderable::InitVertexBuffer()
{
    if (m_pVertexBuffer)
    {
        UINT stride = D3D10_RENDERER->GetVertexBytesSize(m_vertexType);
        UINT offset = 0;

        D3D10_RENDERER->GetDevice()->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);
    }
}

void Renderable::InitIndexBuffer()
{
    if (m_pIndexBuffer)
    {
        D3D10_RENDERER->GetDevice()->IASetIndexBuffer(m_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
    }
}

void Renderable::SetDrawMethod(DrawMethod drawMethod)
{
    m_drawMethod = drawMethod;
}

void Renderable::SetTopology(D3D_PRIMITIVE_TOPOLOGY topology)
{
    m_topology = topology;
}

void Renderable::SetVertexType(VertexLayoutType vertexType)
{
    m_vertexType = vertexType;
}

void Renderable::SetRenderableName(const std::string& sName)
{
    m_sName = sName;
}

std::string Renderable::GetRenderableName()
{
    return m_sName;
}

void Renderable::SetVertexBuffer(ID3D10Buffer* pBuffer)
{
    m_pVertexBuffer = pBuffer;
}

void Renderable::SetIndexBuffer(ID3D10Buffer* pBuffer)
{
    m_pIndexBuffer = pBuffer;
}

ID3D10Buffer* Renderable::GetVertexBuffer()
{
    return m_pVertexBuffer;
}

ID3D10Buffer* Renderable::GetIndexBuffer()
{
    return m_pIndexBuffer;
}

 

Résumé :

Cette classe pourra servir à être dérivée pour afficher vos contenus 3D : image, quad de post-effect, rendu 3D, etc..

Différents types de déclarations de vertex pour DirectX 10

nvidia-quadro-graphics-card

Intro :

Dans nos programmes de rendu 3D nous avons besoin d’utiliser différents types de configurations de vertex.

Prérequis :

– Savoir utiliser les Vertex Input Layouts. Voir cet article.

Explications :

Les voici :

struct PTNVertex
{
    D3DXVECTOR3 position;
    D3DXVECTOR2 texture;
    D3DXVECTOR3 normal;
};

struct PTVertex
{
    D3DXVECTOR3 pos;
    D3DXVECTOR2 texture;
};

struct PNVertex
{
    D3DXVECTOR3 pos;
    D3DXVECTOR3 normal;
};

struct PCVertex
{
    D3DXVECTOR3 position;
    D3DXCOLOR color;
};

struct PTCVertex
{
    D3DXVECTOR3 pos;
    D3DXVECTOR2 texture;
    D3DXCOLOR color;
};

struct PTNTBVertex
{
    D3DXVECTOR3 position;
    D3DXVECTOR2 texture;
    D3DXVECTOR3 normal;
    D3DXVECTOR3 tangent;
    D3DXVECTOR3 binormal;
};

static D3D10_INPUT_ELEMENT_DESC layoutPTN[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutPN[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutPC[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutPTNTB[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "BINORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 44, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutWater[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutPT[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

static D3D10_INPUT_ELEMENT_DESC layoutPTC[] =
{
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

enum VertexLayoutType
{
    PTN_VERTEX, // Position, texture, normal
    PT_VERTEX,
    PN_VERTEX,  // Position, normal
    PC_VERTEX, // Position, couleur
    PTC_VERTEX,
    PTNTB_VERTEX // Position, texture, normal, bitangente et binormale
};

 

Résumé :

Nous avons présenté différents types de configurations de vertex dont vous pouvez vous inspirer.

Obtenir la taille d’une texture ressource shader

equerre

Intro :

Parfois nous avons besoin d’obtenir la taille d’une ressource shader de texture rapidement.

Explications :

Voici comment l’obtenir :

D3DXVECTOR2 GetShaderTextureViewSize(ID3D10ShaderResourceView* texRV)
{
    Assert(texRV);

    ID3D10Texture2D* tex = nullptr;
    texRV->GetResource((ID3D10Resource**)&tex);

    D3D10_TEXTURE2D_DESC texDesc;

    tex->GetDesc(&texDesc);

    return D3DXVECTOR2((float)texDesc.Width, (float)texDesc.Height);
}

 

Résumé :

Nous avons vu comment obtenir les dimensions d’un ressource shader de texture.

Structures de DirectX 10

Intro :

1211804249

DirectX 10 utilise différentes structures pour se configurer.
Il peut être utile de les connaître afin de maitriser l’initialisation de DirectX 10.

[à faire]     D3D10_TEXTURE2D_DESC textureDesc;
D3D10_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
D3D10_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;

Les voici énumérées :

– DXGI_SWAP_CHAIN_DESC :

– utilisée pour configurer la Swap Chain au tout début de l’initialisation de DirectX

– D3D10_TEXTURE2D_DESC :

– utilisée pour décrire une surface 2D

– D3D10_BUFFER_DESC :

– utilisée pour décrire un buffer quelconque

– D3D10_RASTERIZER_DESC :

– utilisée pour configurer l’étape Rasterizer de la chaîne de rendu 3D

– D3D10_VIEWPORT :

– utilisée pour configurer un cadre de vue 3D

– D3D10_SAMPLER_DESC :

– utilisée pour décrire l’échantillonnage d’une texture utilisable dans un shader

– D3D10_COMPARISON_FUNC :

– utilisée pour spécifier la fonction de comparaison entre deux valeurs

D3D10_USAGE :

– utilisée pour spécifier comment est gérée une ressource par le CPU ou le GPU

 –  DXGI_SWAP_CHAIN_DESC :

Explications :

Avant tout configuration de structure, pensez à l’initialiser à zéro avec la macro :

ZeroMemory(&structure_variable, sizeof(Structure));

 

Pour DXGI_SWAP_CHAIN_DESC :

typedef struct DXGI_SWAP_CHAIN_DESC {
  DXGI_MODE_DESC   BufferDesc;
  DXGI_SAMPLE_DESC SampleDesc;
  DXGI_USAGE       BufferUsage;
  UINT             BufferCount;
  HWND             OutputWindow;
  BOOL             Windowed;
  DXGI_SWAP_EFFECT SwapEffect;
  UINT             Flags;
} DXGI_SWAP_CHAIN_DESC;

On la configure ainsi :

DXGI_SWAP_CHAIN_DESC swapChainDesc;

// La taille de la surface en largeur
swapChainDesc.BufferDesc.Width

// La taille de la surface en hauteur
swapChainDesc.BufferDesc.Height

// Le format de la surface
swapChainDesc.BufferDesc.Format

// Le numérateur du taux de rafraichissement - (usuellement 60)
swapChainDesc.BufferDesc.RefreshRate.Numerator

// Le dénominateur du taux de rafraichissement - (usuellement 1)
swapChainDesc.BufferDesc.RefreshRate.Denominator

// Le nombre de buffers dans la Swap Chain
// (y compris le front buffer)
swapChainDesc.BufferCount

// Le nombre de sample par pixel pour l'antialiasing
swapChainDesc.SampleDesc.Count
// Le niveau de qualité de l'image pour l'antialiasing
swapChainDesc.SampleDesc.Quality

// L'handle de la fenêtre à laquelle on va afficher dessus
swapChainDesc.OutputWindow

// Précise si on est en mode fenetré ou plein écran
swapChainDesc.Windowed

// Gère comment DirectX 10 se comporte après l'appel de ->Present(0, 0)
swapChainDesc.SwapEffect

 

Pour D3D10_TEXTURE2D_DESC :

typedef struct D3D10_TEXTURE2D_DESC {
  UINT             Width;
  UINT             Height;
  UINT             MipLevels;
  UINT             ArraySize;
  DXGI_FORMAT      Format;
  DXGI_SAMPLE_DESC SampleDesc;
  D3D10_USAGE      Usage;
  UINT             BindFlags;
  UINT             CPUAccessFlags;
  UINT             MiscFlags;
} D3D10_TEXTURE2D_DESC;

On la configure ainsi :

D3D10_TEXTURE2D_DESC textureDesc;

// Taille de la texture en largeur (l'unité est en texels)
textureDesc.Width

// Taille de la texture en hauteur
textureDesc.Height

// Le nombre de niveau de mipmaps de la texture
textureDesc.MipLevels

// Le nombre de texture dans le tableau
textureDesc.ArraySize

// Le format de la texture
textureDesc.Format

// Le nombre d'échantillon par pixel pour le multisampling
textureDesc.SampleDesc.Count

// Le niveau de qualité de l'image pour le multisampling
textureDesc.SampleDesc.Quality

// Décrit commment est utilisée la texture par le CPU et le GPU
// (voir l'annexe tout en bas)
textureDesc.Usage

// Décrit à quelle étape de la chaîne de rendu 3D est liée cette texture
textureDesc.BindFlags

// Décrit comment le CPU peut accéder à cette ressource texture
// (en lecture ou écriture)
// si 0 : le CPU n'a pas besoin d'accéder à cette ressource
textureDesc.CPUAccessFlags

// Autres options pour cette ressource
textureDesc.MiscFlags

 

Pour D3D10_BUFFER_DESC :

typedef struct D3D10_BUFFER_DESC {
  UINT        ByteWidth;
  D3D10_USAGE Usage;
  UINT        BindFlags;
  UINT        CPUAccessFlags;
  UINT        MiscFlags;
} D3D10_BUFFER_DESC;

On la configure ainsi :

D3D10_BUFFER_DESC bufferDesc;

// Taille du buffer en octet (bytes)
bufferDesc.ByteWidth

// Décrit commment est utilisée la texture par le CPU et le GPU
// (voir l'annexe tout en bas)
bufferDesc.Usage

// Décrit à quelle étape de la chaîne de rendu 3D est liée cette texture
bufferDesc.BindFlags

// Décrit comment le CPU peut accéder à cette ressource texture
// (en lecture ou écriture)
// si 0 : le CPU n'a pas besoin d'accéder à cette ressource
bufferDesc.CPUAccessFlags

// Autres options
bufferDesc.MiscFlags

 

Pour D3D10_RASTERIZER_DESC :

typedef struct D3D10_RASTERIZER_DESC {
  D3D10_FILL_MODE FillMode;
  D3D10_CULL_MODE CullMode;
  BOOL            FrontCounterClockwise;
  INT             DepthBias;
  FLOAT           DepthBiasClamp;
  FLOAT           SlopeScaledDepthBias;
  BOOL            DepthClipEnable;
  BOOL            ScissorEnable;
  BOOL            MultisampleEnable;
  BOOL            AntialiasedLineEnable;
} D3D10_RASTERIZER_DESC;

On la configure ainsi :

D3D10_RASTERIZER_DESC rasterizerDesc;

// Précise le mode de remplissage des primitives
// D3D10_FILL_WIREFRAME ou D3D10_FILL_SOLID
rasterizerDesc.FillMode

// Spécifie si on affiche les triangles qui sont orientés "en face" ou pas
// D3D10_CULL_NONE, D3D10_CULL_FRONT ou D3D10_CULL_BACK
rasterizerDesc.CullMode

// Détermine dans quel sens les triangles sont considérés orientés "en face"
rasterizerDesc.FrontCounterClockwise

// Spécifie le valeur de profondeur DepthBias ajouté à un pixel
rasterizerDesc.DepthBias

// Spécifie la valeur maximale de DepthBias d'un pixel
rasterizerDesc.DepthBiasClamp

// Spécifie l'échelle de grandeur de DepthBias d'un pixel
rasterizerDesc.SlopeScaledDepthBias

// Active ou non la coupure de primitives basée sur la distance
rasterizerDesc.DepthClipEnable

// Active ou non la méthode "rectangle-ciseau"
// qui permet de ne pas rendre les triangles dépassants ce
// rectangle
rasterizerDesc.ScissorEnable

// Active ou non la technique d'antialiasing de multi-échantillonage
rasterizerDesc.MultisampleEnable

// Active ou non la technique de l'antialiasing de ligne
rasterizerDesc.AntialiasedLineEnable

 

Pour D3D10_VIEWPORT :

typedef struct D3D10_VIEWPORT {
  INT   TopLeftX;
  INT   TopLeftY;
  UINT  Width;
  UINT  Height;
  FLOAT MinDepth;
  FLOAT MaxDepth;
} D3D10_VIEWPORT;

On la configure ainsi :

D3D10_VIEWPORT viewPortDesc;

// Abscisse de la position du cadre de vue en partant de haut à gauche
viewPortDesc.TopLeftX 

// Ordonnée basse de la position du cadre de vue en partant de haut à gauche
viewPortDesc.TopLeftY 

// Largeur du cadre de vue
viewPortDesc.Width 

// Hauteur du cadre de vue
viewPortDesc.Height 

// Profondeur minimum du cadre de vue
viewPortDesc.MinDepth 

// Profondeur maximale du cadre de vue
viewPortDesc.MaxDepth

 

Pour D3D10_SAMPLER_DESC :

typedef struct D3D10_SAMPLER_DESC {
  D3D10_FILTER               Filter;
  D3D10_TEXTURE_ADDRESS_MODE AddressU;
  D3D10_TEXTURE_ADDRESS_MODE AddressV;
  D3D10_TEXTURE_ADDRESS_MODE AddressW;
  FLOAT                      MipLODBias;
  UINT                       MaxAnisotropy;
  D3D10_COMPARISON_FUNC      ComparisonFunc;
  FLOAT                      BorderColor[4];
  FLOAT                      MinLOD;
  FLOAT                      MaxLOD;
} D3D10_SAMPLER_DESC;

On la configure ainsi :

D3D10_SAMPLER_DESC samplerDesc;

// Méthode de filtrage utilisée pour échantillonner la texture
samplerDesc.Filter

// Méthode utilisée pour afficher la texture lorsque ses coordonnées U
// sont en dehors de l'intervalle de 0.0f - 1.0f
// (voir l’annexe)
samplerDesc.AddressU 

// Méthode utilisée pour afficher la texture lorsque ses coordonnées V
// sont en dehors de l'intervalle de 0.0f - 1.0f
// (voir l’annexe)
samplerDesc.AddressV

// Méthode utilisée pour afficher la texture lorsque ses coordonnées W
// sont en dehors de l'intervalle de 0.0f - 1.0f
// (voir l’annexe)
samplerDesc.AddressW

// Augmente la valeur préétablis du niveau de mimap de la texture
samplerDesc.MipLODBias

// Spécifie le niveau d'anisotropie de la texture
samplerDesc.MaxAnisotropy

// Spécifie la fonction de comparaison entre chaque données de
// la texture eux-mêmes
samplerDesc.ComparisonFunc

// Spécifie la couleur de bordure si l'on a passé
// D3D10_TEXTURE_ADDRESS_BORDER comme valeur aux AddressU, V ou W
samplerDesc.BorderColor

//
samplerDesc.MinLOD 

//
samplerDesc.MaxLOD

 

Pour D3D10_USAGE :

typedef enum D3D10_USAGE {
  D3D10_USAGE_DEFAULT     = 0,
  D3D10_USAGE_IMMUTABLE   = 1,
  D3D10_USAGE_DYNAMIC     = 2,
  D3D10_USAGE_STAGING     = 3
} D3D10_USAGE;

Voici comment la configurer :


 

Résumé :

Nous avons appris le rôle que joue chaque structure de DirectX 10 afin de mieux comprendre son fonctionnement.

Références :

– DirectX June 2010 SDK Documentation

Un cube tout simple en 3D

cube

Intro :

Voici l’implémentation d’un cube tout simple par la définition de ses vertices et indices.

Il peut être utilisé pour faire des tests ou pour déboguer vos jeux.

Explications :

Dans le fichier Cube.h :

#ifndef CUBE_MESH_H
#define CUBE_MESH_H
 
#include <d3d10.h>
#include <d3dx10.h>

#include "Defines.h"
#include "D3D10Renderer.h"

class Cube
{
public:
    struct PTNVertex
    {
        D3DXVECTOR3 position;
        D3DXVECTOR2 texture;
        D3DXVECTOR3 normal;
 
        PTNVertex()
        {
        }
 
        PTNVertex(D3DXVECTOR3 p, D3DXVECTOR2 t, D3DXVECTOR3 n)
        {
            position = p;
            texture = t;
            normal = n;
        }
    };
 
    Cube();
    virtual ~Cube();
 
    bool Initialize();
 
    void Render(float fTimeSinceLastFrame);
 
private:
    ID3DX10Mesh* m_pMesh;
 
    D3DXMATRIX m_worldMatrix;
    D3DXMATRIX m_viewMatrix;
    D3DXMATRIX m_projMatrix;

    ID3D10EffectMatrixVariable* m_pWorldVariable;
    ID3D10EffectMatrixVariable* m_pViewVariable;
    ID3D10EffectMatrixVariable* m_pProjVariable;

    ID3D10EffectShaderResourceVariable* m_pDiffuseVariable;
    ID3D10ShaderResourceView* m_pDiffuseMap;
     
    ID3D10Effect* m_pEffect;
    ID3D10EffectTechnique* m_pTechnique;
     
    ID3D10InputLayout* m_pVertexLayout;
};
 
#endif

 

Dans le fichier Cube.cpp :

#include "Cube.h"

Cube::Cube() :
m_pMesh(nullptr),
m_pWorldVariable(nullptr),
m_pDiffuseVariable(nullptr),
m_pDiffuseMap(nullptr),
m_pTechnique(nullptr),
m_pEffect(nullptr),
m_pVertexLayout(nullptr)
{
    D3DXMatrixIdentity(&m_worldMatrix);

    D3DXVECTOR3 Eye(0.0f, 5.0f, -6.0f);
    D3DXVECTOR3 At(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 Up(0.0f, 1.0f, 0.0f);
 
    D3DXMatrixLookAtLH(&m_viewMatrix, &Eye, &At, &Up);
 
    D3DXMatrixPerspectiveFovLH(&m_projMatrix, D3DX_PI * 0.25f,
        (float) D3D10_RENDERER->GetViewportWidth() / (float)D3D10_RENDERER->GetViewportHeight(),
        0.1f, 100.0f);
}
 
Cube::~Cube()
{
    SAFE_RELEASE(m_pMesh);
}
 
bool Cube::Initialize()
{
    PTNVertex v[24];
     
    v[0] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    v[1] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    v[2] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    v[3] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
 
    v[4] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) );
    v[5] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) );
    v[6] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( 0.0f, -1.0f, 0.0f ) );
    v[7] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( 0.0f, -1.0f, 0.0f ));
 
    v[8] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) );
    v[9] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) );
    v[10] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) );
    v[11] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( -1.0f, 0.0f, 0.0f ) );
     
    v[12] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) );
    v[13] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f) );
    v[14] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 1.0f, 0.0f, 0.0f ) );
    v[15] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),    D3DXVECTOR3( 1.0f, 0.0f, 0.0f) );
 
    v[16] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, -1.0f) );
    v[17] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 0.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f) );
    v[18] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 1.0f, 1.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) );
    v[19] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR2( 0.0f, 1.0f ),  D3DXVECTOR3( 0.0f, 0.0f, -1.0f ) );
 
    v[20] = PTNVertex( D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) );
    v[21] = PTNVertex( D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 0.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) );
    v[22] = PTNVertex( D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 1.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f) );
    v[23] = PTNVertex( D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR2( 0.0f, 1.0f ), D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) );
     
    unsigned int i[36] = { 3,1,0,
                           2,1,3,
                           6,4,5,
                           7,4,6,
                           11,9,8,
                           10,9,11,
                           14,12,13,
                           15,12,14,
                           19,17,16,
                           18,17,19,
                           22,20,21,
                           23,20,22
                        };    
                         
     
    D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
        { "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 20, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };

    HRESULT hr;
    hr = D3DX10CreateMesh(D3D10_RENDERER->GetDevice(), layout,  3, "POSITION", 24, 36/3, D3DX10_MESH_32_BIT, &m_pMesh);
 
    hr = m_pMesh->SetVertexData(0, v);
 
    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }
 
    hr = m_pMesh->SetIndexData(i, 36);
 
    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }
 
    hr = m_pMesh->CommitToDevice();
 
    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }
 
    ID3D10Blob* pBlob = nullptr;
 
    hr = D3DX10CreateEffectFromFile(L"Cube.fx", nullptr, nullptr, "fx_4_0",
                                    D3D10_SHADER_ENABLE_STRICTNESS | D3D10_SHADER_DEBUG, 0,
                                    D3D10_RENDERER->GetDevice(), nullptr, nullptr, &m_pEffect, &pBlob, nullptr);
    if (FAILED(hr))
    {
        if (pBlob)
        {
            // Affiche l'erreur de compilation du shader
            MessageBoxA(nullptr, (PCSTR)pBlob->GetBufferPointer(), "Erreur dans le fichier shader", MB_ICONHAND | MB_OK);
        }
        else
        {
            ShowMessageBoxDXError(hr);
        }
 
        return false;
    }
 
    m_pTechnique = m_pEffect->GetTechniqueByName("Render");
 
    m_pWorldVariable = m_pEffect->GetVariableByName("World")->AsMatrix();
    m_pViewVariable = m_pEffect->GetVariableByName("View")->AsMatrix();
    m_pProjVariable = m_pEffect->GetVariableByName("Projection")->AsMatrix();
    m_pDiffuseVariable = m_pEffect->GetVariableByName("txDiffuse")->AsShaderResource();
 
    D3D10_PASS_DESC PassDesc;
    m_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
    hr = D3D10_RENDERER->GetDevice()->CreateInputLayout(layout, 3, PassDesc.pIAInputSignature,
                                         PassDesc.IAInputSignatureSize, &m_pVertexLayout);
    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

     // On charge la texture
    hr = D3DX10CreateShaderResourceViewFromFile(D3D10_RENDERER->GetDevice(), L"box.jpg", nullptr, nullptr, &m_pDiffuseMap, nullptr);

    if (FAILED(hr))
    {
        ShowMessageBoxDXError(hr);
        return false;
    }

    return true;
}
 
void Cube::Render(float fTimeSinceLastFrame)
{
    D3D10_RENDERER->GetDevice()->IASetInputLayout(m_pVertexLayout);

    // Pour faire tourner le cube autour de l'axe Y
    static float r = 0;
    D3DXMatrixRotationY(&m_worldMatrix, r);
    r += fTimeSinceLastFrame * 0.6f;

    m_pWorldVariable->SetMatrix((float*)&m_worldMatrix);
    m_pViewVariable->SetMatrix((float*)&m_viewMatrix);
    m_pProjVariable->SetMatrix((float*)&m_projMatrix);
    m_pDiffuseVariable->SetResource(m_pDiffuseMap);

    D3D10_TECHNIQUE_DESC techDesc;
    m_pTechnique->GetDesc(&techDesc);

    for(uint32 p = 0; p < techDesc.Passes; p++)
    {
        m_pTechnique->GetPassByIndex(p)->Apply(0);

        m_pMesh->DrawSubset(0);
    }
}

 

Résumé :

Nous avons implémenter le code pour afficher un petit cube.

Voici les fichiers sources : Simple Cube.zip.

Principe du Z-buffer

zbuffer

Intro :

L’utilisation du Z-buffer est une méthode consistant à afficher dans le bon ordre de profondeur les primitives géométriques envoyées au rendu 3D.

Prérequis :

– Comprendre les principales étapes de rendu de DirectX : voir cet article

Explications :

La technique du Z-buffer consiste à enregistrer pour chaque pixel une valeur de profondeur dans un tampon dédié (en l’occurrence le Z-buffer). Ce tampon a la même dimension que celui du frame buffer. Ces valeurs dans le tampon varient de 0.0f à 1.0f.

Lors de la transformation du repère 3D (celui des meshes / modèles) vers le repère 2D (celui de l’écran) nous avons bien les pixels sur les coordonnées (x, y) conservées mais nous avons perdu l’information sur la profondeur (z) ; il faut la recalculer par un algorithme adjacent.

 

1) Comment ça fonctionne ?

Avant qu’un pixel est affiché dans le tampon de rendu, sa valeur de profondeur est comparée
par sa valeur correspondante actuelle dans le Z-buffer. Si le pixel a sa valeur de profondeur inférieure ou égale alors le pixel est affiché et cette valeur est enregistrée dans le Z-buffer ; autrement le pixel est ignoré.

La carte graphique compare deux profondeurs (Z) une à une, et n’affiche que le pixel le plus proche de la caméra. Ensuite DirectX va automatiquement ignorer l’affichage des triangles cachés par d’autres triangles.

2) Comment le paramétrer ?

Le calcul effectué par le Z-buffer peut être ignoré et désactiver par un appel de l’API DirectX (voir cet article). Lorsqu’il est ignoré par DirectX, les derniers éléments dessinés seront affiché par dessus tous les autres.

Il faut noter aussi qu’à chaque tour de boucle le Z-buffer est réinitialisé et recalculé par cet appel :

m_pDevice->Clear(...)


Résumé :

Nous avons expliqué quel est le rôle du tampon de profondeur (le Z-buffer) dans le rendu 3D.
En effet cette étape permet de cacher les triangles placés à l’arrière d’autres triangles moins proches de la caméra.

Références :

– http://fr.wikipedia.org/wiki/Z-buffer

– http://www.toymaker.info/Games/html/z-buffer.html

Les principales fonctions de la bibliothèque D3DX (DirectX 10)

jigsawpuzzle

Intro :

La bibliothèque D3DX (D3DX utility library) implémente plusieurs fonctions courantes afin de faciliter certaines tâches d’utilisation de l’API 3D DirectX 10.

Nous verrons les objets de cette bibliothèque suivant :

– ID3DX10Sprite

– ID3DX10Font

– ID3DX10Mesh

Prérequis :

– Savoir comment initialiser correctement DirectX 10 (voir ces articles)

Explications :

 


 

ID3DX10Sprite :

Permet de simplifier l’affichage d’images 2D.

On créé cet objet avec la fonction : D3DX10CreateSprite()

J’ai déjà expliqué son utilisation dans cet article (dans la première méthode)

 


 

ID3DX10Font :

Permet d’afficher du texte à l’écran avec une police de font donnée.

On déclare les objets ID3DX10Font et ID3DX10Sprite :

ID3DX10Font* m_pFont;
ID3DX10Sprite* m_pSprite;

On initialise plus loin ces objets comme suit :

// Il faut créer un objet ID3DX10Sprite
D3DX10CreateSprite(m_pd3dDevice, 0, &m_pSprite);

/* Détaille les attributs de cette police d'affichage */
D3DX10_FONT_DESC fd;

// Définit la hauteur d'un caractère de cette police
fd.Height = 30;
// Définit la largeur d'un caractère de cette police
fd.Width = 18;
fd.Weight = 0;
fd.MipLevels = 4;
// En italique ou non
fd.Italic = false;
fd.CharSet = OUT_DEFAULT_PRECIS;
fd.Quality = DEFAULT_QUALITY;
fd.PitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
// Définit le nom de la police à utiliser
wcscpy(fd.FaceName, L"Impact");
 
D3DX10CreateFontIndirect(m_pd3dDevice, &fd, &m_pFont);

 

Dans la boucle de rendu on ajoute :


EnableZBuffer(false);

m_pSprite->Begin(D3DX10_SPRITE_SAVE_STATE);
     m_pFont->DrawText(m_pSprite, L"Hello World!", -1, &rectangle, DT_NOCLIP, color);
m_pSprite->End();

EnableZBuffer(true);

Le paramètre D3DX10_SPRITE_SAVE_STATE permet de restaurer les états du rendu 3D après l’appel End() comme ils étaient configurés avant l’appel Begin().

On désactive l’écriture sur le Z-Buffer pour préserver la profondeur de ce qui est affiché avant l’appel à m_pFontText().

 


 

ID3DX10Mesh :

 

Les Vertex Input Layouts

 

nvidia-quadro-graphics-card

Intro  :

Dans le cas basique, et en général, un vertex a pour attribut seulement sa position. Il existe cependant un mécanisme où il est possible de rajouter d’autres attributs comme : une normale, une couleur, des coordonnées de textures, etc… Je vais tenter de vous expliquer ce mécanisme.

Explications :

Avant tout, lors de l’étape de rendu Input Assembler (voir cet article) les données géométriques présentes dans la mémoire vidéo (composées de ces attributs) sont reçues puis traitées par le Vertex Shader.

Dans DirectX 10, la configuration de ces attributs s’opère sur l’initialisation d’une structure appelée D3D10_INPUT_ELEMENT_DESC.

Ici en tant qu’exemple cette structure (appelée dans le jargon anglais « Vertex Input Layout ») s’implémente ainsi :


// Défini un tableau de composition d'entrée (nommée Input Layout)
D3D10_INPUT_ELEMENT_DESC layout[] =
{
    { L"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};

Elle permet de définir en mémoire vidéo la façon dont sont agencées les vertex.
Remarque : La taille d’un vertex en mémoire équivaut à la taille de cette structure implémentée.

Une structure correspondante est aussi utilisée dans notre programme :

struct SimpleVertex
{
    D3DXVECTOR3 Pos;
};

 


 

Voici les variables de cette fameuse structure :

typedef struct D3D10_INPUT_ELEMENT_DESC {
  LPCSTR                     SemanticName;
  UINT                       SemanticIndex;
  DXGI_FORMAT                Format;
  UINT                       InputSlot;
  UINT                       AlignedByteOffset;
  D3D10_INPUT_CLASSIFICATION InputSlotClass;
  UINT                       InstanceDataStepRate;
} D3D10_INPUT_ELEMENT_DESC;

Paramètres :

SemanticName :

Nom de la « sémantique » de la variable.

Une « sémantique » est une chaîne de caractère qui indique dans quel but la variable doit être utilisée.

Ici son rôle est la position donc on met : « POSITION ».
On peut mettre aussi « NORMAL », « COLOR », « TEXCOORD », etc…

SemanticIndex :

C’est un index (un nombre entier) qui désigne le numéro d’un sémantique qui a déjà été déclaré.

Par exemple, si on déclare deux textures avec sémantique « TEXTURE », on mettra comme SemanticIndex pour le premier champ « 0 » et pour le deuxième champ « 1 ».

Format :

Le format définit le type de données qui sera utilisé pour cet élément.

Par exemple, pour DXGI_FORMAT_R32G32B32_FLOAT : il s’agit d’un attribut avec comme type un flottant et 8 octets alloués par couleur.

InputSlot :

Dans l’étape Input Assembler on peut définir jusqu’à 16 Vertex buffer qui peuvent être utilisés par la carte graphique avant d’être envoyés au Vertex Shader.

Ici on spécifie quel Vertex buffer doit être utilisé par cet attribut.

AlignedByteOffset :

Ce champ dit à la carte graphique quel l’emplacement mémoire pour commencer à aller chercher les données pour cet attribut.
Indiquez D3D10_APPEND_ALIGNED_ELEMENT pour que cet emplacement soit défini de manière automatique.

InputSlotClass :

Cet attribut est utilisé pour gérer l’Instancing. Voir cet article.

InstanceDataStepRate :

Cet attribut est utilisé pour gérer l’Instancing. Voir cet article.

Au final on peut créer cette composition de vertex par le biais de cette méthode : ID3D10Device::CreateInputLayout.

 


 

 

Résumé :

La déclaration d’une structure / composition de vertex permet à la carte graphique de regrouper les informations utiles concernant les vertex en mémoire vidéo avant d’être traités et envoyés au Vertex Shader.

Références :

– http://msdn.developpez.com/direct3d/10/tutoriels/base/

– https://msdn.microsoft.com/en-us/library/windows/desktop/bb205316%28v=vs.85%29.aspx

– https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647%28v=vs.85%29.aspx

Index buffer et Vertex buffer

AMD_6990

Intro :

Les notions d’Index buffer et de Vertex buffer sont fondamentales dans la compréhension des étapes de rendu 3D de modèles / meshes. Ils sont appelés sommets et d’indices en français.

Prérequis :

– Aucun

Vertex :

D’abord, qu’est-ce qu’un vertex ?

On peut imaginer un vertex comme un simple point dans un espace 3D. Ce sont ces vertex qui nous permettront de construire les primitives de base (points, lignes, triangles). Et c’est ces primitives nous permettrons de construire des modèles / objets dans le rendu 3D.

Index / Indice :

Qu’est-ce qu’un index (indice) ? Il faut noter que ce sont juste des nombres que l’on va utiliser par groupe de 3 pour représenter l’ordre d’utilisation d’un triangle.

Autrement dit, c’est un triplet d’index qui sert à indiquer à la carte graphique dans quel ordre sont utilisés les vertex affichés par celle-ci. Ces index peuvent servir à réutiliser les vertices précédemment déclarés.

Vertex buffer :

Un Vertex buffer est simplement une interface stockant les informations sur les vertices d’un modèle du jeu. C’est un tableau situé dans la mémoire de la carte graphique.

On peut stocker dans un vertex buffer des informations : comme la position, les vecteurs normales, les coordonnées d’une texture ou encore même la couleur, voir même n’importe quoi…

Index buffer :

De même que le Vertex buffer,  c’est une interface qui stocke les indices présents dans un tableau contigu.


Déclaration de vertex :

Pour que la carte graphique comprenne comment sont agencées les données d’un vertex buffer, il faut déclarer une structure correspondante. On appel cela une « déclaration de vertex ».

 


 

Avant tout il faut définir une structure correspondante et représentative du type de Vertex :

struct SimpleVertex
{
    D3DXVECTOR3 Position;
    D3DXVECTOR3 Color;
};


Code C++ d’implémentation d’un Vertex Buffer statique :

D3D10_BUFFER_DESC bufferDesc;

bufferDesc.Usage            = D3D10_USAGE_DEFAULT;
bufferDesc.ByteWidth        = sizeof( SimpleVertex ) * 3;
bufferDesc.BindFlags        = D3D10_BIND_VERTEX_BUFFER;
bufferDesc.CPUAccessFlags   = 0;
bufferDesc.MiscFlags        = 0;

ID3D10Buffer* pVertexBuffer = nullptr;

SimpleVertex vertices[] =
{
    D3DXVECTOR3( 0.0f, 0.5f, 0.5f ),
    D3DXVECTOR3( 0.0f, 0.0f, 0.5f ),
    D3DXVECTOR3( 0.5f, -0.5f, 0.5f ),
    D3DXVECTOR3( 0.5f, 0.0f, 0.0f ),
    D3DXVECTOR3( -0.5f, -0.5f, 0.5f ),
    D3DXVECTOR3( 0.0f, 0.5f, 0.0f ),
};

D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;

D3D10_RENDERER->GetDevice()->CreateBuffer( &bufferDesc, &InitData, &pVertexBuffer);


Code C++ d’implémentation d’un Index Buffer :

ID3D10Buffer pIndexBuffer = nullptr;

unsigned int indices[] = { 0, 1, 2 };

D3D10_BUFFER_DESC bufferDesc;
bufferDesc.Usage           = D3D10_USAGE_DEFAULT;
bufferDesc.ByteWidth       = sizeof( unsigned int ) * 3;
bufferDesc.BindFlags       = D3D10_BIND_INDEX_BUFFER;
bufferDesc.CPUAccessFlags  = 0;
bufferDesc.MiscFlags       = 0;

D3D10_SUBRESOURCE_DATA InitData;

InitData.pSysMem = indices;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;

D3D10_RENDERER->GetDevice()->CreateBuffer(&bufferDesc, &InitData, &pIndexBuffer);


Résumé :

Pour afficher un modèle dans un rendu 3D on a besoin de définir comment il est représenté : c’est-à-dire par deux tableaux d’informations de Vertex et d’Indices et une déclaration de vertex.

 

Les principales étapes de rendu de DirectX 10

Intro :

Le rendu graphique de DirectX 10 consiste en plusieurs étapes.

Prérequis :

Savoir utiliser et implémenter DirectX 10.

Explications :

Voici un schéma qui les résume :

220px-D3D_Pipeline.svg

Les rectangles carrés indiquent que l’étape de rendu ne peut pas être configurée par un script ou programme HLSL.

A l’inverse, les rectangles arrondies indiquent que l’étape de rendu peut être configurée par un script ou programme HLSL.

Voir cet article si vous ne savez ce qu’est le HLSL.

 


 

Étape Input Assembler :

Cette étape prépare les données géométriques à la chaîne de rendu 3D. Les données des Vertex Buffers et des Indexes Buffers sont agencées d’une certaine façon dans la mémoire de la carte graphique et à partir de ceux-ci, l’étape Input Assembler créée les primitives géométriques correspondantes (triangles, lignes, points, etc…) afin de les passer au Vertex Shader.

Étape Vertex Shader :

Cette étape permet de modifier les vertices en entrée. Elle consiste à transformer ces vertices depuis les coordonnées Object-space vers les coordonnées Clip-Space ; c’est-à-dire les coordonnées du modèle d’origine vers les coordonnes du cadre de vue de l’écran 2D.
Elle sert aussi à changer la position des vertex dans le repère 3D. Elle est responsable aussi d’effectuer les animations des modèles / personnages du jeu et permet aussi d’effectuer un rendu basique de lumière (en l’occurence du per-pixel ligthing).

Voir le Lexique si vous ne comprenez pas tous ces termes.

Elle traite un vertex à la fois : c’est-à-dire que cette étape est exécutée en boucle pour chaque vertex.

Les données en sortie de cette étape vers le Vertex shader (qui est en fait une fonction toute simple avec entrée et sortie) peuvent être : les coordonnées des textures, la couleur des vertex, etc…

Étape Geometry Shader :

Cette étape optionnelle permet de modifier complètement la géométrie des modèles / meshes.
Au lieu de recevoir vertex par vertex cette étape reçoit une primitive totalement assemblée (une ligne ou un triangle par exemple) ; de là on peut générer d’autres primitives à la volée. Le Geometry Shader peut ignorer une primitive ou en générer de nouvelles.

Étape Stream-Output Stage :

Génère le flux des primitives géométriques depuis la mémoire vers l’étape Rasterizer.

Étape Rasterizer :

Cette étape permet de rassembler les primitives afin de les envoyer au Pixel Shader. Elle s’occupe aussi de couper les primitives qui dépassent le cadre de vue de l’écran.

Étape Pixel Shader :

Cette étape s’occupe de modifier l’affichage de chaque pixel et pour chacun de ceux-ci elle renvoie une couleur en sortie.

Étape Output Merger :

Cette étape s’occupe quel pixel doit être affichée à l’écran. Les pixels générés sont calculés sur la base du Pixel Shader, du contenu des Render Targerts, du contenu du Depth Buffer et du contenu du Stencil Buffer.

 

Résumé :

Nous avons énuméré toutes les étapes du rendu 3D de DirectX 10.

Références :

– Pipeline Stages (Direct3D 10) – DirectX10 June 2010 SDK Documentation

Initialiser rapidement DirectX 10.1 avec DXUT

1342671301_50721

Intro :

DXUT est un framework qui vous permet d’initialiser de manière plus rapide et plus simple DirectX 10 à travers des fonctions toutes prêtes.

Prérequis :

Savoir un peu initialiser DirectX.

Exemples :

Voici un exemple d’utilisation :


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    DXUTSetCallbackD3D10DeviceAcceptable( IsD3D10DeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );

    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackKeyboard( OnKeyboard );
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );

    DXUTInit( true, true, NULL );
    DXUTSetCursorSettings( true, true );
    DXUTCreateWindow( L"DXUT - Introduction" );
    DXUTCreateDevice( true, 640, 480 );
    DXUTMainLoop();

    return DXUTGetExitCode();
}

On remarque bien que quelques lignes suffisent pour démarrer DirectX 10 avec DXUT.

Vous remarquerez aussi que l’on insère des fonctions en tant que paramètres : ce sont des fonctions callbacks (c’est-à-dire des fonctions qui sont appelées de manière interne par DirectX).

Explications :

Initialisation :


DXUTInit(bool, bool, WCHAR*, bool);

Le premier argument indique à DXUT d’interprêter ou non les arguments passés en ligne de comande :

BasicHLSL.exe -windowed -width:400 -height:300

Le deuxième argument indique à DXUT d’afficher ou non une boite de dialogue s’il une erreur à lieu.

Les deux autres arguments ne sont pas nécessaires pour comprendre.

Curseur de la souris :

Voici l’instruction pour configurer le curseur de souris.

DXUTSetCursorSettings(bool, bool);

Le premier paramètre indique à DXUT que le curseur doit ou non
être affichée en mode plein écran.

Le premier paramètre indique à DXUT que le curseur doit
être restreint ou non de traverser les bords de la fenêtre en mode plein écran.

Création d’une fenêtre :


DXUTCreateWindow(const WCHAR, HINSTANCE, HICON, HMENU, INT, INT);

Le premier argument spécifie le titre de la fenêtre.

Le deuxième argument spécifie l’handle de la fenêtre (on peut le mettre à NULL).

Le troisième argument spécifie à la fenêtre quelle icone utiliser (on peut le mettre à NULL aussi).

Les deux autres arguments ne sont pas nécessaires pour comprendre.

Création du device :


DXUTCreateDevice(bool, int, int); 

Le premier argument indique ou non si la fenêtre créée doit être en plein écran ou fenêtrée.

Les deux autres arguments indiquent la résolution (au mieux) de la fenêtre de rendu.

Par ailleurs, après cette instruction, DXUT choisira automatiquement la version de DirectX : D3D9 ou D3D10, selon les capacités du matériel.

Rendu :

Au final l’Instruction pour démarrer la boucle de rendu :


DXUTMainLoop();


Les fonctions Callbacks :

Maintenant pour utiliser DXUT on doit créer un fichier Callbacks.h où mettrez le code des fonctions nécessaire à DXUT :


ID3D10InputLayout* g_pVertexLayout = NULL;

struct SimpleVertex
{
    D3DXVECTOR3 Pos;
    D3DXVECTOR3 Colour;
};

bool CALLBACK IsD3D10DeviceAcceptable(UINT, UINT, D3D10_DRIVER_TYPE,
                                       DXGI_FORMAT, bool, void*);

HRESULT CALLBACK OnD3D10CreateDevice(ID3D10Device*, const DXGI_SURFACE_DESC*,
                                      void*);

HRESULT CALLBACK OnD3D10ResizedSwapChain(ID3D10Device*, IDXGISwapChain*,
                                          const DXGI_SURFACE_DESC*, void*);

void CALLBACK OnD3D10FrameRender(ID3D10Device*, double, float, void*);

void CALLBACK OnD3D10ReleasingSwapChain(void*);

void CALLBACK OnD3D10DestroyDevice(void*);

LRESULT CALLBACK MsgProc(HWND, UINT, WPARAM, LPARAM,
                         bool*, void*);

void CALLBACK OnKeyboard(UINT, bool, bool, void*);

void CALLBACK OnFrameMove(double, float, void*);

bool CALLBACK ModifyDeviceSettings(DXUTDeviceSettings*, void*);

 

Vous mettrez toutes les fonctions énumérées ici prochainement dans un fichier DXUTIntroduction.cpp

La première fonction callback :


// N'est pas utilisée
bool CALLBACK IsD3D10DeviceAcceptable(UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType,
                                      DXGI_FORMAT BufferFormat, bool bWindowed, void* pUserContext)
{
    return true;
}

 

La deuxième fonction callback :

// Initialize le rendu DirectX
HRESULT CALLBACK OnD3D10CreateDevice(ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBufferSurfaceDesc,
                                      void* pUserContext)
{
    HRESULT hr = S_OK;

    // Définit le schéma d'entrée
    D3D10_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
    };
    UINT numElements = sizeof(layout) / sizeof(layout[0]);

    // Créé le schéma d'entrée
    D3D10_PASS_DESC PassDesc;
    g_pTechnique->GetPassByIndex(0)->GetDesc(&PassDesc);
    V_RETURN( pd3dDevice->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature,
                                             PassDesc.IAInputSignatureSize, &g_pVertexLayout));

    // Set the input layout
    pd3dDevice->IASetInputLayout(g_pVertexLayout);

    SimpleVertex vertices[] =
    {
        { D3DXVECTOR3( -1.0f, 1.0f, -1.0f ), D3DXVECTOR4( 0.0f, 0.0f, 1.0f, 1.0f ) },
        { D3DXVECTOR3( 1.0f, 1.0f, -1.0f ), D3DXVECTOR4( 0.0f, 1.0f, 0.0f, 1.0f ) },
        { D3DXVECTOR3( 1.0f, 1.0f, 1.0f ), D3DXVECTOR4( 0.0f, 1.0f, 1.0f, 1.0f ) },
        { D3DXVECTOR3( -1.0f, 1.0f, 1.0f ), D3DXVECTOR4( 1.0f, 0.0f, 0.0f, 1.0f ) },
        { D3DXVECTOR3( -1.0f, -1.0f, -1.0f ), D3DXVECTOR4( 1.0f, 0.0f, 1.0f, 1.0f ) },
        { D3DXVECTOR3( 1.0f, -1.0f, -1.0f ), D3DXVECTOR4( 1.0f, 1.0f, 0.0f, 1.0f ) },
        { D3DXVECTOR3( 1.0f, -1.0f, 1.0f ), D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 1.0f ) },
        { D3DXVECTOR3( -1.0f, -1.0f, 1.0f ), D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f ) },
    };

    D3D10_BUFFER_DESC bd;
    bd.Usage = D3D10_USAGE_DEFAULT;
    bd.ByteWidth = sizeof(SimpleVertex) * 24;
    bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;
    bd.MiscFlags = 0;
    D3D10_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = vertices;
    V_RETURN( pd3dDevice->CreateBuffer(&bd, &InitData, &g_pVertexBuffer));

    // Définit le vertex buffer
    UINT stride = sizeof(SimpleVertex);
    UINT offset = 0;
    pd3dDevice->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);

    // Set primitive topology
    pd3dDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    return S_OK;
}

 

La troisième fonction callback :

HRESULT CALLBACK OnD3D10ResizedSwapChain(ID3D10Device* pd3dDevice, IDXGISwapChain* pSwapChain,
                                          const DXGI_SURFACE_DESC* pBufferSurfaceDesc, void* pUserContext)
{
    return S_OK;
}

 

La quatrième fonction callback :

// Appelée à chaque étape de boucle de rendu
void CALLBACK OnD3D10FrameRender(ID3D10Device* pd3dDevice, double fTime,
                                 float fElapsedTime, void* pUserContext)
{
    float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };
    ID3D10RenderTargetView* pRTV = DXUTGetD3D10RenderTargetView();
    pd3dDevice->ClearRenderTargetView( pRTV, ClearColor );

    ID3D10DepthStencilView* pDSV = DXUTGetD3D10DepthStencilView();
    pd3dDevice->ClearDepthStencilView( pDSV, D3D10_CLEAR_DEPTH, 1.0, 0 );

    pd3dDevice->Draw(3, 0);
}

 

La cinquième fonction callback :

// N'est pas utilisée
void CALLBACK OnD3D10ReleasingSwapChain(void* pUserContext)
{
}

 

La sixième fonction callback :


// Détruit les composants de DirectX
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
    g_pVertexLayout->Release();
    g_pVertexLayout = NULL.
}

 

La septième fonction callback :


// N'est pas utilisée
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
    return true;
}

 

La huitième fonction callback :


LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing,
                          void* pUserContext )
{
    return 0;
}

 

La dernière fonction callback :


void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
    if( bKeyDown )
    {
        switch( nChar )
        {
            case VK_F1: // Change as needed                
                break;
        }
    }
}

 

Résumé :

DXUT vous permet de restreindre d’initialiser de manière laborieuse DirectX 10.
Vous pouvez retrouver le code source de ce petit tutoriel ici.

Références :

Documentation DirectX SDK June 2010