LoginSignup
2
2

More than 5 years have passed since last update.

C++でDirectX12を使用して三角形の色を変更しよう

Last updated at Posted at 2018-12-10

DirectX12で定数バッファに関しての覚書

C++でDirectX12を使って三角形を描画しよう
https://qiita.com/sanoh/items/75917a0375bc1d1cc01c

■Setp1:ピクセルシェーダの変更

2018-12-10 (4).png
色を後から指定して、それで三角形を塗りつぶします。

PixelShader.hlsl
float4 Color;

float4 main() : SV_TARGET
{
    return Color;
}

■Step2:DX12Rendererの修正

DX12Renderer.cpp
#include <fstream>
#include "DX12Renderer.h"

DX12Renderer::DX12Renderer(HWND hwnd, int window_width, int window_height) :
    _window_hwnd(hwnd),
    _window_width(window_width),
    _window_height(window_height) {

    UINT flagsDXGI = 0;
    ID3D12Debug* debug = nullptr;
    HRESULT hr;
#if _DEBUG
    D3D12GetDebugInterface(IID_PPV_ARGS(&debug));
    if (debug) {
        debug->EnableDebugLayer();
        debug->Release();
    }
    flagsDXGI |= DXGI_CREATE_FACTORY_DEBUG;
#endif
    hr = CreateDXGIFactory2(flagsDXGI, IID_PPV_ARGS(_factory.ReleaseAndGetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    ComPtr<IDXGIAdapter> adapter;
    hr = _factory->EnumAdapters(0, adapter.GetAddressOf());
    if (FAILED(hr)) {
        return;
    }
    // デバイス生成.
    // D3D12は 最低でも D3D_FEATURE_LEVEL_11_0 を要求するようだ.
    hr = D3D12CreateDevice(adapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(_device.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドアロケータを生成.
    hr = _device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(_command_allocator.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドキューを生成.
    D3D12_COMMAND_QUEUE_DESC desc_command_queue;
    ZeroMemory(&desc_command_queue, sizeof(desc_command_queue));
    desc_command_queue.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
    desc_command_queue.Priority = 0;
    desc_command_queue.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
    hr = _device->CreateCommandQueue(&desc_command_queue, IID_PPV_ARGS(_command_queue.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }
    // コマンドキュー用のフェンスを準備.
    _fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
    hr = _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(_queue_fence.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // スワップチェインを生成.
    DXGI_SWAP_CHAIN_DESC desc_swap_chain;
    ZeroMemory(&desc_swap_chain, sizeof(desc_swap_chain));
    desc_swap_chain.BufferCount = 2;
    desc_swap_chain.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc_swap_chain.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    desc_swap_chain.OutputWindow = _window_hwnd;
    desc_swap_chain.SampleDesc.Count = 1;
    desc_swap_chain.Windowed = TRUE;
    desc_swap_chain.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
    desc_swap_chain.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    hr = _factory->CreateSwapChain(_command_queue.Get(), &desc_swap_chain, (IDXGISwapChain**)_swap_chain.GetAddressOf());
    if (FAILED(hr)) {
        return;
    }
    // コマンドリストの作成.
    hr = _device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, _command_allocator.Get(), nullptr, IID_PPV_ARGS(_command_list.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // ディスクリプタヒープ(RenderTarget用)の作成.
    D3D12_DESCRIPTOR_HEAP_DESC desc_heap;
    ZeroMemory(&desc_heap, sizeof(desc_heap));
    desc_heap.NumDescriptors = 2;
    desc_heap.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
    desc_heap.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
    hr = _device->CreateDescriptorHeap(&desc_heap, IID_PPV_ARGS(_descriptor_heap.GetAddressOf()));
    if (FAILED(hr)) {
        return;
    }

    // レンダーターゲット(プライマリ用)の作成.
    UINT stride_handle_bytes = _device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
    for (UINT i = 0; i < desc_swap_chain.BufferCount; ++i) {
        hr = _swap_chain->GetBuffer(i, IID_PPV_ARGS(_render_target[i].GetAddressOf()));
        if (FAILED(hr)) {
            return;
        }
        _rtv_handle[i] = _descriptor_heap->GetCPUDescriptorHandleForHeapStart();
        _rtv_handle[i].ptr += i * stride_handle_bytes;
        _device->CreateRenderTargetView(_render_target[i].Get(), nullptr, _rtv_handle[i]);
    }
    //  リソースの初期化
    ResourceSetup();
}

DX12Renderer::~DX12Renderer() {
    if (_g_vertex_shader.binaryPtr) {
        free(_g_vertex_shader.binaryPtr);
    }
    if (_g_pixel_shader.binaryPtr) {
        free(_g_pixel_shader.binaryPtr);
    }
    CloseHandle(_fence_event);
}

BOOL    DX12Renderer::ResourceSetup() {
    HRESULT hr;
    //  頂点情報
    MyVertex vertices_array[] = {
        { 0.0f, 1.0f, 0.0f },
        { -1.0f,-1.0f, 0.0 },
        { +1.0f,-1.0f, 0.0 },
    };
    // PipelineStateのための RootSignature の作成.
    D3D12_ROOT_SIGNATURE_DESC desc_root_signature;
    ZeroMemory(&desc_root_signature, sizeof(desc_root_signature));
    desc_root_signature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
    D3D12_ROOT_PARAMETER rootParameters[1]; //  固定バッファがあるよ<新規>
    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;
    desc_root_signature.NumParameters = 1;
    desc_root_signature.pParameters = rootParameters;
    ComPtr<ID3DBlob> root_sig_blob, error_blob;
    hr = D3D12SerializeRootSignature(&desc_root_signature, D3D_ROOT_SIGNATURE_VERSION_1, root_sig_blob.GetAddressOf(), error_blob.GetAddressOf());
    hr = _device->CreateRootSignature(0, root_sig_blob->GetBufferPointer(), root_sig_blob->GetBufferSize(), IID_PPV_ARGS(_root_signature.GetAddressOf()));
    if (FAILED(hr)) {
        return FALSE;
    }

    // コンパイル済みシェーダーの読み込み.
    FILE* fpVS = nullptr;
    fopen_s(&fpVS, "VertexShader.cso", "rb");
    if (!fpVS) {
        return FALSE;
    }
    fseek(fpVS, 0, SEEK_END);
    _g_vertex_shader.size = ftell(fpVS);
    rewind(fpVS);
    _g_vertex_shader.binaryPtr = malloc(_g_vertex_shader.size);
    fread(_g_vertex_shader.binaryPtr, 1, _g_vertex_shader.size, fpVS);
    fclose(fpVS);
    fpVS = nullptr;
    FILE* fpPS = nullptr;
    fopen_s(&fpPS, "PixelShader.cso", "rb");
    if (!fpPS) {
        return FALSE;
    }
    fseek(fpPS, 0, SEEK_END);
    _g_pixel_shader.size = ftell(fpPS);
    rewind(fpPS);
    _g_pixel_shader.binaryPtr = malloc(_g_pixel_shader.size);
    fread(_g_pixel_shader.binaryPtr, 1, _g_pixel_shader.size, fpPS);
    fclose(fpPS);
    fpPS = nullptr;

    // 今回のための頂点レイアウト.
    D3D12_INPUT_ELEMENT_DESC desc_input_elements[] = {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    };
    // PipelineStateオブジェクトの作成.
    D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline_state;
    D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_state;
    ZeroMemory(&desc_pipeline_state, sizeof(desc_pipeline_state));
    desc_pipeline_state.VS.pShaderBytecode = _g_vertex_shader.binaryPtr;
    desc_pipeline_state.VS.BytecodeLength = _g_vertex_shader.size;
    desc_pipeline_state.PS.pShaderBytecode = _g_pixel_shader.binaryPtr;
    desc_pipeline_state.PS.BytecodeLength = _g_pixel_shader.size;
    desc_pipeline_state.SampleDesc.Count = 1;
    desc_pipeline_state.SampleMask = UINT_MAX;
    desc_pipeline_state.InputLayout.pInputElementDescs = desc_input_elements;
    desc_pipeline_state.InputLayout.NumElements = _countof(desc_input_elements);
    desc_pipeline_state.pRootSignature = _root_signature.Get();
    desc_pipeline_state.NumRenderTargets = 1;
    desc_pipeline_state.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
    desc_pipeline_state.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    desc_pipeline_state.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
    desc_pipeline_state.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
    desc_pipeline_state.RasterizerState.DepthClipEnable = TRUE;
    desc_pipeline_state.RasterizerState.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
    for (int i = 0; i < _countof(desc_state.BlendState.RenderTarget); ++i) {
        desc_pipeline_state.BlendState.RenderTarget[i].BlendEnable = FALSE;
        desc_pipeline_state.BlendState.RenderTarget[i].SrcBlend = D3D12_BLEND_ONE;
        desc_pipeline_state.BlendState.RenderTarget[i].DestBlend = D3D12_BLEND_ZERO;
        desc_pipeline_state.BlendState.RenderTarget[i].BlendOp = D3D12_BLEND_OP_ADD;
        desc_pipeline_state.BlendState.RenderTarget[i].SrcBlendAlpha = D3D12_BLEND_ONE;
        desc_pipeline_state.BlendState.RenderTarget[i].DestBlendAlpha = D3D12_BLEND_ZERO;
        desc_pipeline_state.BlendState.RenderTarget[i].BlendOpAlpha = D3D12_BLEND_OP_ADD;
        desc_pipeline_state.BlendState.RenderTarget[i].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
    }
    desc_pipeline_state.DepthStencilState.DepthEnable = FALSE;

    hr = _device->CreateGraphicsPipelineState(&desc_pipeline_state, IID_PPV_ARGS(_pipeline_state.GetAddressOf()));
    if (FAILED(hr)) {
        return FALSE;
    }

    // 頂点データの作成.
    D3D12_HEAP_PROPERTIES heap_props;
    D3D12_RESOURCE_DESC   desc_resource;
    ZeroMemory(&heap_props, sizeof(heap_props));
    ZeroMemory(&desc_resource, sizeof(desc_resource));
    heap_props.Type = D3D12_HEAP_TYPE_UPLOAD;
    heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heap_props.CreationNodeMask = 0;
    heap_props.VisibleNodeMask = 0;
    desc_resource.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    desc_resource.Width = sizeof(vertices_array);
    desc_resource.Height = 1;
    desc_resource.DepthOrArraySize = 1;
    desc_resource.MipLevels = 1;
    desc_resource.Format = DXGI_FORMAT_UNKNOWN;
    desc_resource.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    desc_resource.SampleDesc.Count = 1;

    hr = _device->CreateCommittedResource(
        &heap_props,
        D3D12_HEAP_FLAG_NONE,
        &desc_resource,
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(_vertex_buffer.GetAddressOf())
    );
    if (FAILED(hr)) {
        return FALSE;
    }
    if (!_vertex_buffer) {
        return FALSE;
    }
    // 頂点データの書き込み
    void* mapped = nullptr;
    hr = _vertex_buffer->Map(0, nullptr, &mapped);
    if (FAILED(hr)) {
        return FALSE;
    }
    memcpy(mapped, vertices_array, sizeof(vertices_array));
    _vertex_buffer->Unmap(0, nullptr);
    _buffer_position.BufferLocation = _vertex_buffer->GetGPUVirtualAddress();
    _buffer_position.StrideInBytes = sizeof(MyVertex);
    _buffer_position.SizeInBytes = sizeof(vertices_array);

    // 定数バッファ Float4 分で確保.<新規>
    D3D12_HEAP_PROPERTIES heapProps;
    D3D12_RESOURCE_DESC   descResourceVB;
    ZeroMemory(&heapProps, sizeof(heapProps));
    ZeroMemory(&descResourceVB, sizeof(descResourceVB));
    heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
    heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
    heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
    heapProps.CreationNodeMask = 0;
    heapProps.VisibleNodeMask = 0;
    descResourceVB.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
    descResourceVB.Width = sizeof(XMFLOAT4);
    descResourceVB.Height = 1;
    descResourceVB.DepthOrArraySize = 1;
    descResourceVB.MipLevels = 1;
    descResourceVB.Format = DXGI_FORMAT_UNKNOWN;
    descResourceVB.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
    descResourceVB.SampleDesc.Count = 1;

    hr = _device->CreateCommittedResource(
        &heapProps,
        D3D12_HEAP_FLAG_NONE,
        &descResourceVB,
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(_constant_buffer.GetAddressOf())
    );
    if (FAILED(hr)) {
        return FALSE;
    }

    hr = _constant_buffer->Map(0, nullptr, &mapped);
    if (FAILED(hr)) {
        return FALSE;
    }
    XMFLOAT4 mtx;
    XMVECTOR draw_color = XMVectorSet(1.0f, 1.0f, 0.0f, 1.0f);
    XMStoreFloat4(&mtx, draw_color);

    memcpy(mapped, &mtx, sizeof(mtx));
    _constant_buffer->Unmap(0, nullptr);

    return TRUE;
}

void DX12Renderer::SetResourceBarrier(ID3D12GraphicsCommandList* in_command_list, ID3D12Resource* in_resource, D3D12_RESOURCE_STATES in_before, D3D12_RESOURCE_STATES in_after)
{
    D3D12_RESOURCE_BARRIER desc_barrier;
    ZeroMemory(&desc_barrier, sizeof(desc_barrier));
    desc_barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
    desc_barrier.Transition.pResource = in_resource;
    desc_barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
    desc_barrier.Transition.StateBefore = in_before;
    desc_barrier.Transition.StateAfter = in_after;
    in_command_list->ResourceBarrier(1, &desc_barrier);
}

void DX12Renderer::WaitForCommandQueue(ID3D12CommandQueue* in_command_queue) {
    static UINT64 frames = 0;
    _queue_fence->SetEventOnCompletion(frames, _fence_event);
    in_command_queue->Signal(_queue_fence.Get(), frames);
    WaitForSingleObject(_fence_event, INFINITE);
    frames++;
}

//  描画
HRESULT DX12Renderer::Render() {
    // まずはターゲットのクリア処理
    float clearColor[4] = { 1.0f,0.0f,0.0f,0.0f };
    D3D12_VIEWPORT viewport;
    viewport.TopLeftX   = 0; viewport.TopLeftY = 0;
    viewport.Width      = (FLOAT)_window_width;
    viewport.Height     = (FLOAT)_window_height;
    viewport.MinDepth   = 0;
    viewport.MaxDepth   = 1;

    //  定数バッファに色の設定<新規>
    XMVECTOR draw_color = XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f);  //青
    void* pCB;
    _constant_buffer->Map(0, nullptr, &pCB);
    if (pCB) {
        XMStoreFloat4((XMFLOAT4*)pCB, draw_color);
        _constant_buffer->Unmap(0, nullptr);
    }

    int target_index = _swap_chain->GetCurrentBackBufferIndex();
    SetResourceBarrier(
        _command_list.Get(),
        _render_target[target_index].Get(),
        D3D12_RESOURCE_STATE_PRESENT,
        D3D12_RESOURCE_STATE_RENDER_TARGET);

    // レンダーターゲットのクリア処理.
    _command_list->RSSetViewports(1, &viewport);
    _command_list->ClearRenderTargetView(_rtv_handle[target_index], clearColor, 0, nullptr);
    //-------------------------------------------
    //  
    D3D12_RECT rect = { 0, 0, _window_width, _window_height };
    _command_list->RSSetScissorRects(1, &rect);
    _command_list->OMSetRenderTargets(1, &_rtv_handle[target_index], TRUE, nullptr);

    // 頂点データをセット.
    _command_list->SetGraphicsRootSignature(_root_signature.Get());

    //  定数バッファ設定<新規>
    _command_list->SetGraphicsRootConstantBufferView(0, _constant_buffer->GetGPUVirtualAddress());

    //  シェーダー設定
    _command_list->SetPipelineState(_pipeline_state.Get());
    //
    _command_list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    _command_list->IASetVertexBuffers(0, 1, &_buffer_position);
    _command_list->DrawInstanced(3, 1, 0, 0);
    //-------------------------------------------
    // Presentする前の準備.
    SetResourceBarrier(
        _command_list.Get(),
        _render_target[target_index].Get(),
        D3D12_RESOURCE_STATE_RENDER_TARGET,
        D3D12_RESOURCE_STATE_PRESENT);
    //-------------------------------------------
    _command_list->Close();

    // 上記で積んだコマンドを実行する.
    ID3D12CommandList* pCommandList = _command_list.Get();
    _command_queue->ExecuteCommandLists(1, &pCommandList);
    _swap_chain->Present(1, 0);

    WaitForCommandQueue(_command_queue.Get());
    _command_allocator->Reset();
    _command_list->Reset(_command_allocator.Get(), nullptr);
    return S_OK;
}

■Step3:実行

青くなりました。
2018-12-10 (5).png

2
2
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
2
2