9
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C++でDirectX12を使って三角形を描画しよう

Last updated at Posted at 2018-12-07

DirectX12で三角形を出す覚書
「C++を使ってDirectX12を初期化」を参照
https://qiita.com/sanoh/items/11b339daf2ff3a4d5e88

■Setp1:DX12Renderer.cppの編集

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;
	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 (SUCCEEDED(hr)) {
		memcpy(mapped, vertices_array, sizeof(vertices_array));
		_vertex_buffer->Unmap(0, nullptr);
	}
	if (FAILED(hr)) {
		return FALSE;
	}
	_buffer_position.BufferLocation	= _vertex_buffer->GetGPUVirtualAddress();
	_buffer_position.StrideInBytes	= sizeof(MyVertex);
	_buffer_position.SizeInBytes	= sizeof(vertices_array);

	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;

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

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

	// 頂点データをセット.
	_command_list->SetGraphicsRootSignature(_root_signature.Get());
	//	シェーダー設定
	_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[targetIndex].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;
}
DX12Renderer.h
#pragma once

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

using namespace DirectX;
using namespace Microsoft::WRL;

#ifndef _countof
#define _countof __crt_countof
#endif
// 今回のための頂点レイアウト.
struct MyVertex {
	float x, y, z;
};

class DX12Renderer {
public:
	static constexpr int RTV_NUM = 2;

public:
	DX12Renderer(HWND hwnd, int window_width, int window_height);
	~DX12Renderer();
	HRESULT Render();
private:
	HWND    _window_hwnd;
	int     _window_width;
	int     _window_height;

	HANDLE  _fence_event;

	ComPtr<ID3D12Device> _device;
	ComPtr<ID3D12CommandQueue> _command_queue;
	ComPtr<ID3D12CommandAllocator> _command_allocator;
	ComPtr<ID3D12GraphicsCommandList> _command_list;
	ComPtr<IDXGISwapChain3> _swap_chain;
	ComPtr<ID3D12Fence>  _queue_fence;
	ComPtr<IDXGIFactory3> _factory;
	ComPtr<ID3D12DescriptorHeap> _descriptor_heap;
	ComPtr<ID3D12Resource> _render_target[2];
	D3D12_CPU_DESCRIPTOR_HANDLE _rtv_handle[2];

	void SetResourceBarrier(ID3D12GraphicsCommandList* commandList, ID3D12Resource* resource, D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after);
	void WaitForCommandQueue(ID3D12CommandQueue* pCommandQueue);
private:
	struct ShaderObject {
		void* binaryPtr;
		int   size;
	};
	ComPtr<ID3D12RootSignature> _root_signature;
	ComPtr<ID3D12PipelineState> _pipeline_state;
	ShaderObject _g_vertex_shader;
	ShaderObject _g_pixel_shader;
	ComPtr<ID3D12Resource>		_vertex_buffer;
	D3D12_VERTEX_BUFFER_VIEW	_buffer_position;

	BOOL	ResourceSetup();
};

■Step2:頂点シェーダーの作成

VertexShader.hlslを作成します。
「test02」を右クリックするとサブメニューが出てくるので「追加(D)」「新しい項目(W)」を選択します。
2018-12-07 (1).png
「HLSL」の「頂点シェーダーファイル(.hlsl)」を選択します。名前は「VertexShader.hlsl」
2018-12-09 (3).png

VertexShader.hlsl
float4 main(float4 pos : POSITION) : SV_POSITION
{
	return pos;
}

2018-12-07 (2).png
このままではエラーになってしまうので。プロパティーを開きます。
2018-12-07 (3).png
まずは「全般」のなかの「シェーダー種類」を「頂点シェーダー(/vs)」に
そして今回は「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
2018-12-07 (14).png
実行時に「VertexShader.hlsl」は「VertexShader.cso」にコンバートされるので出力先を設定します。
2018-12-07 (5).png

$(OutDir)..\..\%(Filename).cso

を設定し「OK」を押してください。

■Step3:ピクセルシェーダーの作成

VertexShader.hlslと同様にPixelShader.hlslを作成します。
2018-12-10.png

PixelShader.hlsl
float4 main() : SV_TARGET
{
	return float4(1.0, 1.0, 1.0, 1.0);
}

こちらもプロパティーを開き
まずは「全般」のなかの「シェーダー種類」を「ピクセルイェーダー(/ps)」に
そして「シェーダーモデル」を「Shader Model 5.0(/5.0)」に設定してください。
2018-12-07 (9).png
コンパイルごの出力パスも
2018-12-07 (10).png

$(OutDir)..\..\%(Filename).cso

を設定し「OK」を押してください。

■Step4:実行

実行すると、シェーダーもコンパイルされこんな感じです。
2018-12-10 (1).png

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?