LoginSignup
34
35

More than 5 years have passed since last update.

DirectX12を勉強してみた

Last updated at Posted at 2016-01-30

注意!

この記事の内容は古いものとなっています。新しいものを書いていますので、そちらにお願いします。
DirectX12で3D描画


DirectXを全くいじったことがない初心者が無謀にもDirectX12を勉強してみたのでその備忘録的なものとして...
参考資料や知識などが少なく(参考資料よりも知識が無いほうが痛い),DirectX11の本などを読みながらプログラムを作成したので,間違いなど多分に含んでいる恐れがあります.間違いや勘違いがあった場合(というか絶対あります)教えていただけるとありがたいです.(お手柔らかにお願いしますm(_ _)m)

環境

Windows10 Pro
Visual Studio 2015 Community
SDK 10.0.10586.0

1. D3D12Managerクラスの宣言

Direct3D12をまとめたクラスを作ります.ウインドウハンドルとウインドウのサイズは外からもらってきます.ウインドウは今までどおりWinAPIで作成してあります.

D3D12Manager
#ifndef DEF_D3D12MANAGER_H
#define DEF_D3D12MANAGER_H

#include <Windows.h>
#include <d3d12.h>
#include <d3d12shader.h>
#include <dxgi1_4.h>
#include <d3dcompiler.h>
#include <DirectXMath.h>

#define RTV_BUFFER_NUM (2)
#define SAFE_RELEASE(p) {if(p){(p)->Release(); (p) = nullptr;}}

using namespace DirectX;

class D3D12Manager{
public:
    typedef struct Vertex3D{
        XMFLOAT3 Position;
        XMFLOAT3 Color;
    }Vertex3D;

    typedef struct ConstantBufferData{
        XMFLOAT4X4 WVP;
    }ConstantBufferData;

public:
    D3D12Manager(HWND hWnd, int WindowWidth, int WindowHeight);
    ~D3D12Manager();
    int CreateDevice();
    int CreateCommandQueue();
    int CreateSwapChain();
    int CreateRenderTarget();
    int CreateDepthStencilBuffer();
    int CreateCommandList();
    int CreateConstantBuffer();
    int CreateIndexBuffer();
    int CreateVertexBuffer();
    int CreateRootSignature();
    int CompileShader();
    int CreatePipelineStateObject();
    int SetVertexData();
    int WaitForPreviousFrame();
    int SetResourceBarrier(D3D12_RESOURCE_STATES BeforeState, D3D12_RESOURCE_STATES AfterState);
    int PopulateCommandList();
    int Render();
    int SetPlaneData();

private:
    HWND m_hWnd;
    int m_iWindowWidth;
    int m_iWindowHeight;
    UINT64 Frames;
    UINT RTVIdx;
    IDXGIFactory4   *pDXGIFactory;
    IDXGIAdapter3   *pAdapter;
    IDXGISwapChain3 *pSwapChain;
    ID3D12Device                    *pDevice;
    ID3D12CommandQueue              *pComandQueue;
    HANDLE                           handleFenceEvent;
    ID3D12Fence                     *pQueueFence;
    ID3D12DescriptorHeap            *pDescriptorHeapRTV;
    ID3D12Resource                  *pRenderTarget[RTV_BUFFER_NUM];
    D3D12_CPU_DESCRIPTOR_HANDLE      handleRTV[RTV_BUFFER_NUM];
    ID3D12Resource                  *pDepthBuffer;
    ID3D12DescriptorHeap            *pDescriptorHeapDSB;
    D3D12_CPU_DESCRIPTOR_HANDLE      handleDSV;
    D3D12_RECT                       RectScissor;
    D3D12_VIEWPORT                   Viewport;
    ID3D12GraphicsCommandList   *pCommandList;
    ID3D12CommandAllocator      *pCmdAllocator;
    ID3D12Resource              *pConstantBuffer;
    ID3D12DescriptorHeap        *pDescriptorHeapCBV;
    D3D12_CPU_DESCRIPTOR_HANDLE  handleCBV;
    ID3D12Resource              *pIndexBuffer;
    D3D12_INDEX_BUFFER_VIEW      IndexView;
    ID3D12Resource              *pVertexBuffer;
    D3D12_VERTEX_BUFFER_VIEW     VertexView;
    ID3D12PipelineState *pPipelineState;
    ID3D12RootSignature *pRootSignature;
    ID3DBlob            *pRootSignatureBlob;
    ID3DBlob            *pErrorBlob;
    ID3DBlob            *VertexShader;
    ID3DBlob            *PixelShader;
};

#endif

コンストラクタではメソッドを順番に呼んで初期化し,ViewPortとシザー矩形の設定をします.

D3D12Manager
D3D12Manager::D3D12Manager(HWND hWnd, int WindowWidth, int WindowHeight):
    m_hWnd(hWnd),
    m_iWindowWidth(WindowWidth),
    m_iWindowHeight(WindowHeight),
    Frames(1),
    RTVIdx(0),
    pDXGIFactory(nullptr),
    pAdapter(nullptr),
    pSwapChain(nullptr),
    pDevice(nullptr),
    pComandQueue(nullptr),
    handleFenceEvent(nullptr),
    pQueueFence(nullptr),
    pDescriptorHeapRTV(nullptr),
    handleRTV{0},
    pDepthBuffer(nullptr),
    pDescriptorHeapDSB(nullptr),
    handleDSV{0},
    RectScissor{0},
    Viewport{0},
    pCommandList(nullptr),
    pCmdAllocator(nullptr),
    pConstantBuffer(nullptr),
    pIndexBuffer(nullptr),
    IndexView{0},
    pVertexBuffer(nullptr),
    VertexView{0},
    pPipelineState(nullptr),
    pRootSignature(nullptr),
    pRootSignatureBlob(nullptr),
    pErrorBlob(nullptr),
    VertexShader(nullptr),
    PixelShader(nullptr){
    CreateDevice();
    CreateCommandQueue();
    CreateSwapChain();
    CreateRenderTarget();
    CreateDepthStencilBuffer();
    CreateCommandList();
    CreateBuffer();
    CreateRootSignature();
    CompileShader();
    CreatePipelineStateObject();
    SetVertexData();

    Viewport.TopLeftX = 0.f; 
    Viewport.TopLeftY = 0.f;
    Viewport.Width    = (FLOAT)m_iWindowWidth;
    Viewport.Height   = (FLOAT)m_iWindowHeight;
    Viewport.MinDepth = 0.f;
    Viewport.MaxDepth = 1.f;

    RectScissor.top    = 0;
    RectScissor.left   = 0;
    RectScissor.right  = m_iWindowWidth;
    RectScissor.bottom = m_iWindowHeight;
}

デストラクタではオブジェクトの開放をします.

~D3D12Manager
D3D12Manager::~D3D12Manager(){
    SAFE_RELEASE(VertexShader);
    SAFE_RELEASE(PixelShader);
    SAFE_RELEASE(pPipelineState);
    SAFE_RELEASE(pErrorBlob);
    SAFE_RELEASE(pRootSignatureBlob);
    SAFE_RELEASE(pRootSignature);

    SAFE_RELEASE(pVertexBuffer);
    SAFE_RELEASE(pIndexBuffer);
    SAFE_RELEASE(pConstantBuffer);
    SAFE_RELEASE(pCmdAllocator);
    SAFE_RELEASE(pCommandList);

    SAFE_RELEASE(pDepthBuffer);
    SAFE_RELEASE(pDescriptorHeapDSB);
    for(int i = RTV_BUFFER_NUM - 1; i >= 0; --i){
        SAFE_RELEASE(pRenderTarget[i]);
    }
    SAFE_RELEASE(pDescriptorHeapRTV);
    SAFE_RELEASE(pQueueFence);
    SAFE_RELEASE(pComandQueue);
    SAFE_RELEASE(pDevice);

    SAFE_RELEASE(pSwapChain);
    SAFE_RELEASE(pAdapter);
    SAFE_RELEASE(pDXGIFactory);
}

2. デバイスの準備

まずはデバイスの作成からです.ファクトリを生成し,アダプタを見つけ,デバイスを作成するという流れです.デバッグモードの場合,デバッグレイヤーを有効にする処理も入っています.デバッグモードでファクトリを作成する場合はフラグにDXGI_CREATE_FACTORY_DEBUGを渡します.DirectX12ではデバイスを作成する際にD3D_FEATURE_LEVEL_11_0以上を要求するようです.

CreateDevice
int D3D12Manager::CreateDevice(){
    HRESULT hr;
    UINT flagsDXGI = 0;

#if defined(_DEBUG)
    ID3D12Debug *dxDebug = nullptr;
    hr = D3D12GetDebugInterface(IID_PPV_ARGS(&dxDebug));
    if(FAILED(hr)){
        return -1;
    }

    //デバッグレイヤーを有効にする
    dxDebug->EnableDebugLayer();
    dxDebug->Release();
    dxDebug = nullptr;

    flagsDXGI |= DXGI_CREATE_FACTORY_DEBUG;
#endif

    //ファクトリの作成
    hr = CreateDXGIFactory2(flagsDXGI, IID_PPV_ARGS(&pDXGIFactory));
    if(FAILED(hr)){
        return -1;
    }


    DXGI_ADAPTER_DESC AdapterDesc;
    //最初に見つかったアダプターを使用する
    hr = pDXGIFactory->EnumAdapters(0, (IDXGIAdapter**)&pAdapter);
    if(SUCCEEDED(hr)){
        pAdapter->GetDesc(&AdapterDesc);
        hr = D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_1, IID_PPV_ARGS(&pDevice));
        if(FAILED(hr)){     
            return -1;
        }
    }else{
        return -1;
    }

    return 0;
}

エラーが返されたときの処理などは真面目に書いていません.また,アウトプットデバイスの取得も行っていません.アダプタも実際はforなどで列挙して適切なものを選ぶようにしたほうがいいかと思います.今回は使用していませんが,AdapterDesc.VendorId == 0x1414 && AdapterDesc.DeviceId == 0x8cの場合,WARPデバイスだそうです(EnumWarpAdapter関数を使ってもWARPデバイスは取得できます).私の環境だとVM環境ではWARPデバイスでしか実行できませんでした.またWARPデバイスではアウトプットの取得もできませんでした.

3. コマンドキューの作成

次にコマンドキューの作成をします.DirectX12からはコマンドがすべて遅延実行されるようになりました.そのコマンドを蓄え,実行するためのオブジェクトがコマンドキューです.次のように作成します.

CreateCommandQueue
int D3D12Manager::CreateCommandQueue(){
    HRESULT hr;

    // コマンドキューを生成.
    D3D12_COMMAND_QUEUE_DESC descCommandQueue;
    ZeroMemory(&descCommandQueue, sizeof(descCommandQueue));
    descCommandQueue.Type     = D3D12_COMMAND_LIST_TYPE_DIRECT;
    descCommandQueue.Priority = 0;
    descCommandQueue.Flags    = D3D12_COMMAND_QUEUE_FLAG_NONE;
    descCommandQueue.NodeMask = 0;
    hr = pDevice->CreateCommandQueue(&descCommandQueue, IID_PPV_ARGS(&pComandQueue));
    if (FAILED(hr)) {
        return -1;
    }

    //コマンドキュー用のフェンスの生成
    handleFenceEvent = CreateEventEx(nullptr, FALSE, FALSE, EVENT_ALL_ACCESS);//CreateEvent(NULL, FALSE, FALSE, NULL);
    if(handleFenceEvent == NULL ){
        return -1;
    }
    hr = pDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&pQueueFence));
    if (FAILED(hr)) {
        return -1;
    }
    return 0;
}

DirectX12ではドローコールはこのCommandQueueに積み込みます.そしてこのCommandQueueはいつ実行が終わるかわかりません(遅延実行のため).なので積んだコマンドが終了しているか確認しなければなりません.ここで作成したフェンスはその実行の終了待ちをするためのものです.

4. スワップチェインの作成

続いて,スワップチェインの作成です.DirectX11とほとんど変わり無いようですが,BufferCountが裏表合わせた数指定しなければなりません.

CreateSwapChain
int D3D12Manager::CreateSwapChain(){
    HRESULT hr = S_OK;
    IDXGISwapChain *pSwapChain = nullptr;
    DXGI_SWAP_CHAIN_DESC DXGISwapChainDesc;
    ZeroMemory(&DXGISwapChainDesc, sizeof(DXGISwapChainDesc));

    DXGISwapChainDesc.BufferDesc.Width  = m_iWindowWidth;
    DXGISwapChainDesc.BufferDesc.Height = m_iWindowHeight;
    DXGISwapChainDesc.OutputWindow      = m_hWnd;
    DXGISwapChainDesc.Windowed          = TRUE;
    DXGISwapChainDesc.BufferUsage       = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    DXGISwapChainDesc.BufferCount       = RTV_BUFFER_NUM;//#define RTV_BUFFER_NUM 2
    DXGISwapChainDesc.SwapEffect        = DXGI_SWAP_EFFECT_FLIP_DISCARD;
    DXGISwapChainDesc.Flags             = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    DXGISwapChainDesc.BufferDesc.RefreshRate.Numerator   = 60;
    DXGISwapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    DXGISwapChainDesc.BufferDesc.Format           = DXGI_FORMAT_R8G8B8A8_UNORM;
    DXGISwapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    DXGISwapChainDesc.BufferDesc.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;
    DXGISwapChainDesc.SampleDesc.Count   = 1;
    DXGISwapChainDesc.SampleDesc.Quality = 0;


    //IDXGISwapChain 型でスワップチェインを作成
    hr = pDXGIFactory->CreateSwapChain(pComandQueue, &DXGISwapChainDesc, &pSwapChain);
    if(FAILED(hr)){return -1;}

    //IDXGISwapChain3 で受け直す
    hr = pSwapChain->QueryInterface(&pSwapChain);
    if(FAILED(hr)){return -1;}

    pSwapChain->Release();
    pSwapChain = nullptr;

    return 0;
}

構造体に値を設定して関数に投げているだけです.始めにIDXGISwapChain型で作成してQueryInterface関数を使用してIDXGISwapChain3型として受け取っています.

5. レンダーターゲットの作成

レンダーターゲットの作成はまずDescriptorHeapの作成から始めます.デスクリプタはDirectX12で初出のものです.DirectX11でいうところのViewに相当するものです.メモリ上のデータを指し示すものです.DescriptorHeapを作成したらレンダーターゲットを作成してDescriptorHeapに登録します.

CreateRenderTarget
int D3D12Manager::CreateRenderTarget(){
    HRESULT hr;
    D3D12_DESCRIPTOR_HEAP_DESC HeapDesc;

    //RTV用DescriptorHeapの作成
    ZeroMemory(&HeapDesc, sizeof(HeapDesc));
    HeapDesc.NumDescriptors = 2;
    HeapDesc.Type           = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    HeapDesc.Flags          = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    HeapDesc.NodeMask       = 0;
    hr = pDevice->CreateDescriptorHeap(&HeapDesc, IID_PPV_ARGS(&pDescriptorHeapRTV));
    if (FAILED(hr)) {
        return -1;
    }

    //レンダーターゲットを作成する処理
    UINT DescriptorHandleIncrementSize = pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    for(UINT i = 0; i < RTV_BUFFER_NUM; ++i){
        //スワップチェインからバッファを受け取る
        hr = pSwapChain->GetBuffer(i, IID_PPV_ARGS(&pRenderTarget[i]));
        if(FAILED(hr)){
            return -1;
        }

        //RenderTargetViewを作成してDescriptorHeapに登録
        handleRTV[i] = pDescriptorHeapRTV->GetCPUDescriptorHandleForHeapStart();
        handleRTV[i].ptr += DescriptorHandleIncrementSize * i;
        pDevice->CreateRenderTargetView(pRenderTarget[i], nullptr, handleRTV[i]);
    }

    return 0;
}

6. DepthBufferの作成

DepthBufferの作成です.3Dオブジェクトの前後関係(深度)を記録するバッファです.

CreateDepthStencilBuffer
int D3D12Manager::CreateDepthStencilBuffer(){
    HRESULT hr;

    D3D12_DESCRIPTOR_HEAP_DESC descDescriptorHeapDSB;
    ZeroMemory(&descDescriptorHeapDSB, sizeof(descDescriptorHeapDSB)); 
    descDescriptorHeapDSB.NumDescriptors = 1; 
    descDescriptorHeapDSB.Type           = D3D12_DESCRIPTOR_HEAP_TYPE_DSV; 
    descDescriptorHeapDSB.Flags          = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; 
    descDescriptorHeapDSB.NodeMask       = 0;
    hr = pDevice->CreateDescriptorHeap(&descDescriptorHeapDSB, IID_PPV_ARGS(&pDescriptorHeapDSB));
    if(FAILED(hr)){
        return -1;
    }

    D3D12_RESOURCE_DESC DepthDesc;
    D3D12_HEAP_PROPERTIES HeapProps;
    D3D12_CLEAR_VALUE ClearValue;
    ZeroMemory(&DepthDesc, sizeof(DepthDesc));
    ZeroMemory(&HeapProps, sizeof(HeapProps));
    ZeroMemory(&ClearValue, sizeof(ClearValue));

    DepthDesc.Dimension          = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    DepthDesc.Width              = m_iWindowWidth;
    DepthDesc.Height             = m_iWindowHeight;
    DepthDesc.DepthOrArraySize   = 1;
    DepthDesc.MipLevels          = 0;
    DepthDesc.Format             = DXGI_FORMAT_R32_TYPELESS;
    DepthDesc.Layout             = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    DepthDesc.SampleDesc.Count   = 1;
    DepthDesc.SampleDesc.Quality = 0;
    DepthDesc.Flags              = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;

    HeapProps.Type                 = D3D12_HEAP_TYPE_DEFAULT;
    HeapProps.CPUPageProperty      = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    HeapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    HeapProps.CreationNodeMask     = 0;
    HeapProps.VisibleNodeMask      = 0;

    ClearValue.Format               = DXGI_FORMAT_D32_FLOAT;
    ClearValue.DepthStencil.Depth   = 1.0f;
    ClearValue.DepthStencil.Stencil = 0;

    hr = pDevice->CreateCommittedResource(&HeapProps, D3D12_HEAP_FLAG_NONE, &DepthDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &ClearValue, IID_PPV_ARGS(&pDepthBuffer));
    if(FAILED(hr)){
        return -1;
    }


    D3D12_DEPTH_STENCIL_VIEW_DESC DSVDesc;
    ZeroMemory(&DSVDesc, sizeof(DSVDesc));
    DSVDesc.ViewDimension      = D3D12_DSV_DIMENSION_TEXTURE2D;
    DSVDesc.Format             = DXGI_FORMAT_D32_FLOAT;
    DSVDesc.ViewDimension      = D3D12_DSV_DIMENSION_TEXTURE2D;
    DSVDesc.Texture2D.MipSlice = 0;
    DSVDesc.Flags              = D3D12_DSV_FLAG_NONE;

    handleDSV = pDescriptorHeapDSB->GetCPUDescriptorHandleForHeapStart();

    pDevice->CreateDepthStencilView(pDepthBuffer, &DSVDesc, handleDSV);

    return 0;
}

7. コマンドリスト・コマンドアロケータ

続いてコマンドリストとコマンドアロケータです.コマンドリストは先程出てきたコマンドキューに渡すコマンドをまとめるオブジェクトです.DirectX12では頂点やインデックスの設定やレンダーターゲットの設定などのドローコールなどはすべてコマンドリストに設定するようになりました.コマンドリストに積まれたコマンドがハードウエアネイティブに変換されたものを蓄えるものがコマンドアロケータです.

CreateCommandList
int D3D12Manager::CreateCommandList(){
    HRESULT hr;

    //コマンドアロケータの作成
    hr = pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&pCmdAllocator));
    if (FAILED(hr)) {
        return -1;
    }

    //コマンドアロケータとバインドしてコマンドリストを作成する
    hr = pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, pCmdAllocator, nullptr, IID_PPV_ARGS(&pCommandList));
    if (FAILED(hr)) {
        return -1;
    }

    return 0;
}

ここで作成したコマンドリストに描画コマンドを設定し,先ほど作成したコマンドキューに流すことで描画が行われます.

8. 定数・頂点・インデックスバッファの作成

3Dオブジェクトの情報を書き込むためのバッファ類を作成します.今回は定数バッファも頂点バッファもインデックスバッファもすべて同じ設定なので,構造体を使いまわして初期化してしまいます.手抜きです.

CreateBuffer
int D3D12Manager::CreateBuffer(){
    HRESULT hr;

    D3D12_HEAP_PROPERTIES HeapPropeties;
    D3D12_RESOURCE_DESC   ResourceDesc;
    ZeroMemory(&HeapPropeties, sizeof(HeapPropeties));
    ZeroMemory(&ResourceDesc, sizeof(ResourceDesc));
    HeapPropeties.Type                 = D3D12_HEAP_TYPE_UPLOAD;
    HeapPropeties.CPUPageProperty      = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    HeapPropeties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    HeapPropeties.CreationNodeMask     = 0;
    HeapPropeties.VisibleNodeMask      = 0;

    ResourceDesc.Dimension        = D3D12_RESOURCE_DIMENSION_BUFFER;
    ResourceDesc.Width            = 256;
    ResourceDesc.Height           = 1;
    ResourceDesc.DepthOrArraySize = 1;
    ResourceDesc.MipLevels        = 1;
    ResourceDesc.Format           = DXGI_FORMAT_UNKNOWN;
    ResourceDesc.Layout           = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    ResourceDesc.SampleDesc.Count   = 1;
    ResourceDesc.SampleDesc.Quality = 0;

    //定数バッファの作成
    hr = pDevice->CreateCommittedResource(&HeapPropeties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pConstantBuffer));
    if(FAILED(hr)){
        return -1;
    }

    //インデックスバッファの作成
    hr = pDevice->CreateCommittedResource(&HeapPropeties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pIndexBuffer));
    if(FAILED(hr)){
        return -1;
    }

    //頂点バッファの作成
    hr = pDevice->CreateCommittedResource(&HeapPropeties, D3D12_HEAP_FLAG_NONE, &ResourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&pVertexBuffer));
    if(FAILED(hr)){
        return -1;
    }
    return 0;
}

9. ルートシグネチャの作成

ルートシグネチャは作成したバッファとシェーダーのレジスタを結びつけるテーブルのようなものです.今回は定数バッファのみを設定しています.テクスチャやサンプラーも設定する場合はD3D12_DESCRIPTOR_RANGEも設定してDescriptorTableに登録しますが,定数バッファはルートパラメータに直接設定できるので今回はそうします.

CreateRootSignature
int D3D12Manager::CreateRootSignature(){
    HRESULT hr;
    D3D12_ROOT_SIGNATURE_DESC RootSignatureDesc;
    D3D12_ROOT_PARAMETER RootParameters[1];

    ZeroMemory(&RootSignatureDesc, sizeof(RootSignatureDesc));
    ZeroMemory(&RootParameters[0], sizeof(RootParameters[0]));

    RootParameters[0].ParameterType             = D3D12_ROOT_PARAMETER_TYPE_CBV;
    RootParameters[0].ShaderVisibility          = D3D12_SHADER_VISIBILITY_ALL;
    RootParameters[0].Descriptor.ShaderRegister = 0;
    RootParameters[0].Descriptor.RegisterSpace  = 0;

    RootSignatureDesc.Flags         = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    RootSignatureDesc.NumParameters = _countof(RootParameters);
    RootSignatureDesc.pParameters   = RootParameters;

    hr = D3D12SerializeRootSignature(&RootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &pRootSignatureBlob, &pErrorBlob);
    if(FAILED(hr)){
        return -1;
    }
    hr = pDevice->CreateRootSignature(0, pRootSignatureBlob->GetBufferPointer(), pRootSignatureBlob->GetBufferSize(), IID_PPV_ARGS(&pRootSignature));
    if(FAILED(hr)){
        return -1;
    }
    return 0;
}

10. シェーダーの読み込み

シェーダーの読み込みです.DirectX11とくらべても特に変わったところは無いようです.D3DCompileFromFile関数を使用し,ID3DBlobへのポインタ型で受け取ります.

CompileShader
int D3D12Manager::CompileShader(){
    HRESULT hr;
#if defined(_DEBUG)
    UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
    UINT compileFlags = 0;
#endif

    hr = D3DCompileFromFile(L"Shaders.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &VertexShader, nullptr);
    if(FAILED(hr)){
        return -1;
    }

    hr = D3DCompileFromFile(L"Shaders.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &PixelShader, nullptr);
    if(FAILED(hr)){
        return -1;
    }

    return 0;
}

シェーダーの中身ですが極小です.特に何もしていません.変換行列の類は別々に受け取ってシェーダー内で計算してしまっています.

Shaders.hlsl
//定数場ファファの値
cbuffer cbBuffer : register(b0){
    float4x4 WVP;
};

struct VS_INPUT{
    float3 Position : POSITION;
    float3 Color    : COLOR;
};

struct PS_INPUT{//(VS_OUTPUT)
    float4 Position : SV_POSITION;
    float4 Color    : COLOR;
};


PS_INPUT VSMain(VS_INPUT input){
    PS_INPUT output;

    float4 pos4 = float4(input.Position, 1.0);
    output.Position = mul(pos4, WVP);
    output.Color    = float4(input.Color, 1.0);

    return output;
}


float4 PSMain(PS_INPUT input) : SV_TARGET{
    return input.Color;
}

11. PipelineStateObject (PSO)の作成

PipelineStateObject の作成です.DirectX11では描画するときにシェーダーやらラスタライザステートやらブレンドステートやらを設定していましたが,それをひとまとめにして管理してしまおうというオブジェクトです.なのでDirectX12ではシェーダーやラスタライザやブレンドステートなどは始めにここで設定して描画時にはこのオブジェクト一つをセットするだけです.このオブジェクトはすべてまとまってしまっているため,描画時に違うパラメータを設定したい場合はその分PipelineStateObject を作成しなければならないようです.

CreatePipelineStateObject
int D3D12Manager::CreatePipelineStateObject(){
    HRESULT hr;

    // 頂点レイアウト.
    D3D12_INPUT_ELEMENT_DESC descInputElements[] = {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0,  0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
        { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    };

    D3D12_GRAPHICS_PIPELINE_STATE_DESC PiplineStateDesc;
    ZeroMemory(&PiplineStateDesc, sizeof(PiplineStateDesc));

    //シェーダーの設定
    PiplineStateDesc.VS.pShaderBytecode = VertexShader->GetBufferPointer();
    PiplineStateDesc.VS.BytecodeLength  = VertexShader->GetBufferSize();
    PiplineStateDesc.HS.pShaderBytecode = nullptr;
    PiplineStateDesc.HS.BytecodeLength  = 0;
    PiplineStateDesc.DS.pShaderBytecode = nullptr;
    PiplineStateDesc.DS.BytecodeLength  = 0;
    PiplineStateDesc.GS.pShaderBytecode = nullptr;
    PiplineStateDesc.GS.BytecodeLength  = 0;
    PiplineStateDesc.PS.pShaderBytecode = PixelShader->GetBufferPointer();
    PiplineStateDesc.PS.BytecodeLength  = PixelShader->GetBufferSize();


    //インプットレイアウトの設定
    PiplineStateDesc.InputLayout.pInputElementDescs = descInputElements;
    PiplineStateDesc.InputLayout.NumElements        = _countof(descInputElements);


    //サンプル系の設定
    PiplineStateDesc.SampleDesc.Count   = 1;
    PiplineStateDesc.SampleDesc.Quality = 0;
    PiplineStateDesc.SampleMask         = UINT_MAX;

    //レンダーターゲットの設定
    PiplineStateDesc.NumRenderTargets = 1;
    PiplineStateDesc.RTVFormats[0]    = DXGI_FORMAT_R8G8B8A8_UNORM;

    //三角形に設定
    PiplineStateDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;


    //ルートシグネチャ
    PiplineStateDesc.pRootSignature = pRootSignature;


    //ラスタライザステートの設定
    PiplineStateDesc.RasterizerState.CullMode              = D3D12_CULL_MODE_NONE;
    PiplineStateDesc.RasterizerState.FillMode              = D3D12_FILL_MODE_SOLID;
    PiplineStateDesc.RasterizerState.FrontCounterClockwise = FALSE;
    PiplineStateDesc.RasterizerState.DepthBias             = 0;
    PiplineStateDesc.RasterizerState.DepthBiasClamp        = 0;
    PiplineStateDesc.RasterizerState.SlopeScaledDepthBias  = 0;
    PiplineStateDesc.RasterizerState.DepthClipEnable       = TRUE;
    PiplineStateDesc.RasterizerState.ConservativeRaster    = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
    PiplineStateDesc.RasterizerState.AntialiasedLineEnable = FALSE;
    PiplineStateDesc.RasterizerState.MultisampleEnable     = FALSE;


    //ブレンドステートの設定
    for(int i = 0; i < _countof(PiplineStateDesc.BlendState.RenderTarget); ++i){
        PiplineStateDesc.BlendState.RenderTarget[i].BlendEnable           = FALSE;
        PiplineStateDesc.BlendState.RenderTarget[i].SrcBlend              = D3D12_BLEND_ONE;
        PiplineStateDesc.BlendState.RenderTarget[i].DestBlend             = D3D12_BLEND_ZERO;
        PiplineStateDesc.BlendState.RenderTarget[i].BlendOp               = D3D12_BLEND_OP_ADD;
        PiplineStateDesc.BlendState.RenderTarget[i].SrcBlendAlpha         = D3D12_BLEND_ONE;
        PiplineStateDesc.BlendState.RenderTarget[i].DestBlendAlpha        = D3D12_BLEND_ZERO;
        PiplineStateDesc.BlendState.RenderTarget[i].BlendOpAlpha          = D3D12_BLEND_OP_ADD;
        PiplineStateDesc.BlendState.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
        PiplineStateDesc.BlendState.RenderTarget[i].LogicOpEnable         = FALSE;
        PiplineStateDesc.BlendState.RenderTarget[i].LogicOp               = D3D12_LOGIC_OP_CLEAR;
    }
    PiplineStateDesc.BlendState.AlphaToCoverageEnable  = FALSE;
    PiplineStateDesc.BlendState.IndependentBlendEnable = FALSE;


    //デプスステンシルステートの設定
    PiplineStateDesc.DepthStencilState.DepthEnable      = TRUE;                             //深度テストあり
    PiplineStateDesc.DepthStencilState.DepthFunc        = D3D12_COMPARISON_FUNC_LESS_EQUAL;
    PiplineStateDesc.DepthStencilState.DepthWriteMask   = D3D12_DEPTH_WRITE_MASK_ALL;
    PiplineStateDesc.DepthStencilState.StencilEnable    = FALSE;                            //ステンシルテストなし
    PiplineStateDesc.DepthStencilState.StencilReadMask  = D3D12_DEFAULT_STENCIL_READ_MASK;
    PiplineStateDesc.DepthStencilState.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;

    PiplineStateDesc.DepthStencilState.FrontFace.StencilFailOp      = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.FrontFace.StencilPassOp      = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.FrontFace.StencilFunc        = D3D12_COMPARISON_FUNC_ALWAYS;

    PiplineStateDesc.DepthStencilState.BackFace.StencilFailOp      = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.BackFace.StencilPassOp      = D3D12_STENCIL_OP_KEEP;
    PiplineStateDesc.DepthStencilState.BackFace.StencilFunc        = D3D12_COMPARISON_FUNC_ALWAYS;

    PiplineStateDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT;

    hr = pDevice->CreateGraphicsPipelineState(&PiplineStateDesc, IID_PPV_ARGS(&pPipelineState));
    if(FAILED(hr)){
        return -1;
    }
    return 0;
}

12. 頂点データの設定

作ったVertexBufferとIndexBufferに頂点データの設定をします.

SetVertexData

int D3D12Manager::SetVertexData(){
    HRESULT hr;
    void *Mapped;

    //頂点データをVertexBufferに書き込む
    Vertex3D Vertices[] = {
        {XMFLOAT4(-1.f,  1.f, 0.f, 1.f), XMFLOAT4(0.f,  0.f, 1.f, 0.f), XMFLOAT2(0.f, 0.f)},
        {XMFLOAT4( 1.f,  1.f, 0.f, 1.f), XMFLOAT4(0.f,  0.f, 1.f, 0.f), XMFLOAT2(1.f, 0.f)},
        {XMFLOAT4( 1.f, -1.f, 0.f, 1.f), XMFLOAT4(0.f,  0.f, 1.f, 0.f), XMFLOAT2(1.f, 1.f)},
        {XMFLOAT4(-1.f, -1.f, 0.f, 1.f), XMFLOAT4(0.f,  0.f, 1.f, 0.f), XMFLOAT2(0.f, 1.f)},
    };
    hr = pVertexBuffer->Map(0, nullptr, &Mapped);
    if (SUCCEEDED(hr)) {
        CopyMemory(Mapped, Vertices, sizeof(Vertices));
        pVertexBuffer->Unmap(0, nullptr);
        Mapped = nullptr;
    }else{
        return -1;
    }
    VertexView.BufferLocation = pVertexBuffer->GetGPUVirtualAddress();
    VertexView.StrideInBytes  = sizeof(Vertex3D);
    VertexView.SizeInBytes    = sizeof(Vertices);


    //インデックスデータをIndexBufferに書き込む
    uint16_t Index[] = {0, 1, 3, 1, 2, 3};
    hr = pIndexBuffer->Map(0, nullptr, &Mapped);
    if (SUCCEEDED(hr)) {
        CopyMemory(Mapped, Index, sizeof(Index));
        pIndexBuffer->Unmap(0, nullptr);
        Mapped = nullptr;
    }else{
        return -1;
    }
    IndexView.BufferLocation = pIndexBuffer->GetGPUVirtualAddress();
    IndexView.SizeInBytes    = sizeof(Index);
    IndexView.Format         = DXGI_FORMAT_R16_UINT;
    return 0;
}

いや~長かった......
これでひとまずオブジェクトの生成は終了です.次から描画に入りたいと思います.

つづく

オブジェクトの作成だけで大変長くなってしまいました.今回はここで一旦終了したいと思います.次回は描画周りをやる予定です.そして何度も申し上げますが,DirectXをいじったことがない人が独学で調べたものなので,間違いを多く含む恐れがありますので,ご了承ください.
次回はこちら


だいぶ長い間記事を放置してしまいました……。
読み返してみると、自分自身全く理解していないまま書いたものなので色々説明足りなかったりコード間違っていたりしてますね^^;
時間が空いたらちゃんとしたものを書こうと思います。

34
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
35