DirectX12の描画処理 #4 [Devlog #007]

Table of Contents

DirectX12を使った描画処理


ポリゴンの描画

ライブラリの導入


DirectXMath

DirectXMath APIは、DirextXアプリケーションに共通する一般的な線形代数およびグラフィックス演算用のSIMD対応のC++関数を提供するライブラリで、ユニバーサルWindowsプラットフォームアプリ、Xboxゲーム開発者向けに設計されている
詳細

DirectXTK

DirectXTK(DirectX Tool Kit)は、マイクロソフト社が提供するDirectX 11と12に対応したゲーム開発を行うためのオープンソースのクラスライブラリであり、XNA Game Framework(2006年に発表)がC++に移植されたもの
DirextXTKの詳細
ユニバーサルWindowsプラットフォーム(UWP)用ゲーム開発の詳細

NuGet

NuGetは、Microsoft .NET環境で使用されるパッケージマネージャーであり、拡張子が.nupkgのzipファイルにはコンパイル済みのコード(DLL)、そのコードに関連する他のファイル、パッケージのバージョン番号などの情報が記述されているマニフェストが含まれる
詳細

DirectXMathの導入

Visual Studioのツール->NuGetパッケージマネージャー->ソリューションのNuGetパッケージの管理で「directxtk12_uwp」を検索して選択、プロジェクトframeworkDX12を含む)にチェックしてインストール

stdafx.h

// stdafx.h

// ~ //

// DirectX Took Kit
#pragma comment(lib, "DirectXTK12.lib")
#include <SimpleMath.h>

// ~ //

Utility.h

// Utility.h

// ~ //

namespace Math = DirectX::SimpleMath;

メッシュ


GraphicsフォルダにMeshフォルダを追加し、Mesh.hMesh.cppを追加

dev-dx12-202308(プロジェクト名)
└─ Source
   ├─ Application
   │  ├─ Application.h
   │  └─ Application.cpp
   ├─ Graphics
   │  ├─ Mesh
   │  │  ├─ Mesh.h
   │  │  └─ Mesh.cpp
   │  ├─ GraphicsDevice.h
   │  └─ GraphicsDevice.cpp
   ├─ System
   │  ├─ Utility
   │  │  ├─ Utility.h
   │  │  └─ Utility.cpp
   │  ├─ Window
   │  │  ├─ Window.h
   │  │  └─ Window.cpp
   │  └─ System.h
   ├─ stdafx.h
   └─ stdafx.cpp

GraphicsDevice.h

GraphicsDeviceクラス

仮想アダプターを返すGetDevice関数を定義

コマンドリストを返すGetCmdList関数を定義

// GraphicsDevice.h

class GraphicsDevice {
public:
	// ~ //

	ID3D12Device8* GetDevice()const {
		return m_pDevice.Get();
	}

	ID3D12GraphicsCommandList6* GetCmdList()const {
		return m_pCmdList.Get();
	}

	// ~ //

Mesh.h

Meshクラス

メッシュの作成を行う関数Create()を宣言(グラフィクスデバイスのポインタを渡す)

インスタンス描画を行うDrawInstanced()を宣言

D3D12_VERTEX_BUFFER_VIEW構造体は頂点バッファーについて説明するビュー
m_vbView.BufferLocationはバッファーのアドレスを識別するD3D12_GPU_VIRTUAL_ADDRESSを指定する
m_vbView.SizeInBytesはバッファーのサイズをバイト単位で指定する
m_vbView.StrideInBytesは各頂点エントリのサイズをバイト単位で指定する
詳細

D3D12_INDEX_BUFFER_VIEW構造体は表示するインデックスバッファーについて説明するビュー
詳細

SimpleMath::Vector3詳細

//Mesh.h

class Mesh {
public:
	void Create(GraphicsDevice* pGraphicsDevice);

	void DrawInstanced()const;

private:
	GraphicsDevice* m_pDevice = nullptr;
	ComPtr<ID3D12Resource>		m_pVBuffer = nullptr;
	ComPtr<ID3D12Resource>		m_pIBuffer = nullptr;
	D3D12_VERTEX_BUFFER_VIEW	m_vbView;
	D3D12_INDEX_BUFFER_VIEW		m_ibView;

	std::array<Math::Vector3, 3> m_vertices;
};

Mesh.cpp

Mesh::Create()関数

D3D12_HEAP_PROPERTIES構造体はヒープのプロパティについて説明する
heapProp.Typeはヒープの種類を指定する値(列挙の詳細
heapProp.CPUPagePropertyはヒープのCPU ページプロパティを指定する値(列挙の詳細
heapProp.MemoryPoolPreferenceはヒープのメモリプールを指定する値(列挙の詳細
詳細

D3D12_RESOURCE_DESC構造体はテクスチャなどのリソースについて説明する
resDesc.Dimensionはリソースの種類を指定(列挙の詳細
resDesc.Widthはリソースの幅を指定する
resDesc.Heightはリソースの高さを指定する
resDesc.DepthOrArraySizeは3Dの場合はリソースの深さを指定し、1Dまたは2Dリソースの配列の場合は配列サイズを指定する
resDesc.MipLevelsはMIPレベルの数を指定する
resDesc.FormatDXGI_FORMATの1つのメンバーを指定する(列挙の詳細
resDesc.SampleDescはリソースのマルチサンプリングパラメーターについて説明する(構造体の詳細
resDesc.SampleDesc.Countはピクセルあたりのマルチサンプルの数を指定する
resDesc.Flagsはリソースを操作するためのオプションを指定する(列挙の詳細
resDesc.Layoutはテクスチャレイアウトオプションを指定する(列挙の詳細
詳細

m_pDevice->GetDevice()->CreateCommittedResource()は、リソースがリソース全体を格納するのに十分な大きさのヒープにマップされるように、リソースと暗黙的なヒープの両方を作成する
▶ 第一引数はリソースのヒープのプロパティを提供するD3D12_HEAP_PROPERTIES構造体へのポインタ
▶ 第二引数はD3D12_HEAP_FLAGSで表すヒープオプション
▶ 第三引数はリソースを記述するD3D12_RESOURCE_DESC構造体へのポインタ
▶ 第四引数はD3D12_RESOURCE_STATESで表すリソースの初期状態(列挙の詳細
▶ 第五引数はクリアカラーの既定値を記述するD3D12_CLEAR_VALUE構造体を指定する(構造体の詳細
▶ 第六引数はリソースインターフェイスのGUID
詳細

ID3D12Device::CreateCommittedResource()

データの塊をGPU上に作成して、CPU側からそのバッファにデータをコピーして頂点情報をGPUから見えるようにする


m_pVBuffer->GetGPUVirtualAddress()は、バッファーリソースのGPU仮想アドレスを返す
詳細

m_pVBuffer->Map()は、リソース内の指定されたサブリソースへのCPUポインタを取得する(ポインタ値をアプリケーションに公開することはできない)
▶ 第一引数はサブリソースのインデックス番号を指定する
▶ 第二引数はアクセスするメモリの範囲を記述するD3D12_RANGE構造体へのポインタ(構造体の詳細
▶ 第三引数はリソースデータへのポインタを受け取るメモリブロックへのポインタ
詳細

m_pVBuffer->Unmap()は、リソース内の指定したサブリソースへのCPUポインタを無効にする
▶ 第一引数はサブリソースのインデックスを指定する
▶ 第二引数はマップ解除するメモリの範囲を記述するD3D12_RANGE構造体へのポインタ
詳細

//Mesh.cpp

void Mesh::Create(GraphicsDevice* pGraphicsDevice) {
	m_pDevice = pGraphicsDevice;

	m_vertices[0] = { -0.75f, -0.75f, 0.0f };
	m_vertices[1] = {   0.0f,  0.75f, 0.0f };
	m_vertices[2] = {  0.75f, -0.75f, 0.0f };

	D3D12_HEAP_PROPERTIES heapProp = {};
	heapProp.Type = D3D12_HEAP_TYPE_UPLOAD;
	heapProp.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
	heapProp.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;

	D3D12_RESOURCE_DESC resDesc = {};
	resDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
	resDesc.Width = sizeof(Math::Vector3) * m_vertices.size();
	resDesc.Height = 1;
	resDesc.DepthOrArraySize = 1;
	resDesc.MipLevels = 1;
	resDesc.Format = DXGI_FORMAT_UNKNOWN;
	resDesc.SampleDesc.Count = 1;
	resDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
	resDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;

	auto hr = m_pDevice->GetDevice()->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_NONE, &resDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_pVBuffer));

	if (FAILED(hr)) {
		assert(0 && "頂点バッファー作成失敗");
		return;
	}

	m_vbView.BufferLocation = m_pVBuffer->GetGPUVirtualAddress();
	m_vbView.SizeInBytes = (UINT)resDesc.Width;
	m_vbView.StrideInBytes = sizeof(Math::Vector3);

	Math::Vector3* vbMap = nullptr;
	{
		m_pVBuffer->Map(0, nullptr, (void**)&vbMap);
		std::copy(std::begin(m_vertices), std::end(m_vertices), vbMap);
		m_pVBuffer->Unmap(0, nullptr);
	}
}
Mesh::DrawInstanced()関数

m_pDevice->GetCmdList()->IASetVertexBuffers()は、頂点バッファーのCPUディスクリプタハンドルを設定する
▶ 第一引数(頂点バッファーの設定を開始するには、デバイスの0から始まる配列にインデックスを作成)
▶ 第二引数は第三引数のm_vbView配列内のビューの数
▶ 第三引数はD3D12_VERTEX_BUFFER_VIEW構造体の配列内の頂点バッファービューを指定する
詳細

m_pDevice->GetCmdList()->DrawInstanced()は、インデックスのないインスタンス化されたプリミティブを描画する
▶ 第一引数は描画する頂点の数
▶ 第二引数は描画するインスタンスの数
▶ 第三引数は最初の頂点のインデックス
▶ 第四引数は頂点バッファーからインスタンスごとのデータを読み取る前に、各インデックスに追加された値
詳細

//Mesh.cpp

void Mesh::DrawInstanced()const
{
	m_pDevice->GetCmdList()->IASetVertexBuffers(0, 1, &m_vbView);
	m_pDevice->GetCmdList()->DrawInstanced(3, 1, 0, 0);
}

System.h

System.hにMesh.hをインクルード

//System.h

#include "Utility/Utility.h"

#include "../Graphics/RTVHeap/RTVHeap.h"	// GraphicsDevice.hよりも先にインクルード

#include "../Graphics/GraphicsDevice.h"

#include "../Graphics/Mesh/Mesh.h"

シェーダー


プロジェクト直下にAsset>Shaderフォルダを追加し、SimpleShader_VS.hlslSimpleShader_PS.hlslを追加

GraphicsフォルダにShaderフォルダを追加し、Shader.hShader.cppを追加

ShaderフォルダにRootSignatureフォルダを追加し、RootSignature.hRootSignature.cppを追加

ShaderフォルダにPipelineフォルダを追加し、Pipeline.hPipeline.cppを追加

dev-dx12-202308(プロジェクト名)
├─ Asset
│  └─ Shader
│     ├─ SimpleShader_VS.hlsl
│     └─ SimpleShader_PS.hlsl
└─ Source
   ├─ Application
   │  ├─ Application.h
   │  └─ Application.cpp
   ├─ Graphics
   │  ├─ Mesh
   │  │  ├─ Mesh.h
   │  │  └─ Mesh.cpp
   │  ├─ Shader
   │  │  ├─ RootSignature
   │  │  │  ├─ RootSignature.h
   │  │  │  └─ RootSignature.cpp
   │  │  ├─ Pipeline
   │  │  │  ├─ Pipeline.h
   │  │  │  └─ Pipeline.cpp
   │  │  ├─ Shader.h
   │  │  └─ Shader.cpp
   │  ├─ GraphicsDevice.h
   │  └─ GraphicsDevice.cpp
   ├─ System
   │  ├─ Utility
   │  │  ├─ Utility.h
   │  │  └─ Utility.cpp
   │  ├─ Window
   │  │  ├─ Window.h
   │  │  └─ Window.cpp
   │  └─ System.h
   ├─ stdafx.h
   └─ stdafx.cpp

SimpleShader_VS.hlsl(頂点シェーダー)

基本的に座標変換の処理が記述され、頂点数分だけ実行される(ここでの処理は、入力として与えられた位置情報をそのまま返す)

x,y,z,wの4つの成分の浮動小数点数が返されるエントリーポイント関数main()

:POSITIONセマンティクスは、この引数が頂点の位置情報を持っていることを示す

:SV_POSITIONセマンティクスは、関数の戻り値がシェーダーの出力位置を示していることを示す

SV_は"頂点シェーダーからラスタライザーへ"のようにプログラマブルなステージから非プログラマブルなステージに情報を投げる場合に付加される接頭辞(System Value:システムバリュー)

// SimpleShader_VS.hlsl

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

SimpleShader_PS.hlsl(ピクセルシェーダー)

ここでは引数に座標情報のみが入ってきている

基本的にライティングの処理が記述され、画面のピクセル分だけ実行される(ここでの処理は、すべてのピクセルを白色で塗りつぶす)

ピクセルシェーダーは色を決定するだけで、塗りつぶすかどうかはラスタライザーが決定する

:SV_TARGETセマンティクスは、関数の戻り値がレンダーターゲット(通常は画面やテクスチャ)に書き込まれる色情報を示していることを示す

// SimpleShader_PS.hlsl

float4 main(float4 pos : SV_POSITION) : SV_TARGET {
	return float4(1.0f, 1.0f, 1.0f, 1.0f);
}

シェーダーはC言語と同様にエントリポイント(C言語でいうところのmain()関数)が必要になり、パイプラインステートというオブジェクトによりエントリポイントの指定が行われる

RootSignature.h

RangeType列挙型クラス

レンジタイプリストを表す
CBV(定数バッファ):マトリックス、ベクトル、スカラー値など
SRV(シェーダーリソース):テクスチャやバッファなどの読み取り専用のデータをシェーダーに供給するためのリソースで、テクスチャをピクセルシェーダーに渡してサンプリングする場合はそのテクスチャのSRVが必要になる
URV(非順序つきアクセスリソース):シェーダーから読み書きが可能なリソースで、コンピュートシェーダーでのデータの並列処理やレンダリングの高度な技術(イメージベースの照明など)において使われる

// RootSignature.h

enum class RangeType {
	CBV,
	SRV,
	UAV,
};
TextureAddressMode列挙型クラス

アドレスモードを表す
Wrap:テクスチャ座標がテクスチャの境界を超えると、座標はテクスチャのサイズに対してモジュロ演算を行い、テクスチャの反対側から再び開始する
Clamp:テクスチャ座標がテクスチャの境界を超えると、座標はテクスチャの境界にクランプ(制限)される

// RootSignature.h

enum class TextureAddressMode {
	Wrap,
	Clamp,
};
D3D12Filter列挙型クラス

テクスチャサンプリング時のフィルタリング方法を指定するための列挙型
Point:サンプリング時に最も近いテクスチャのテクセル(テクスチャのピクセル)の値をそのまま使用する
Linear:サンプリング時に近隣のテクセルの値を線形的に補間して使用する

// RootSignature.h

enum class D3D12Filter {
	Point,
	Linear,
};
RootSignatureクラス

ルートシグネチャの作成を行う関数Create()を宣言
▶ 第一引数はグラフィクスデバイスのポインタを指定する
▶ 第二引数はレンジタイプリストを指定する

ID3D12RootSignatureは、リソースとシェーダーを対応つけるインタフェース(グラフィックスパイプラインにバインドされるリソースを定義するインタフェース)
詳細

ルートシグネチャの取得を行う関数GetRootSignature()を定義

レンジの作成を行う関数CreateRange()を宣言
▶ 第一引数はレンジのポインタを指定する(構造体の詳細
▶ 第二引数はレンジタイプを指定する
▶ 第三引数は登録数

サンプラーの作成を行う関数CreateStaticSampler()を宣言
▶ 第一引数はサンプラーデスクのポインタを指定する(構造体の詳細
▶ 第二引数はアドレスモードを指定する
▶ 第三引数はフィルターを指定する
▶ 第四引数は使用サンプラー数

ID3DBlobは、任意の長さのデータを返すために使用されるインタフェース

// RootSignature.h

class RootSignature {
public:
	void Create(GraphicsDevice* pGraphicsDevice, const std::vector<RangeType>& rangeTypes);

	ID3D12RootSignature* GetRootSignature() { return m_pRootSignature.Get(); }

private:
	void CreateRange(D3D12_DESCRIPTOR_RANGE& pRange, RangeType type, int count);

	void CreateStaticSampler(D3D12_STATIC_SAMPLER_DESC& samplerDesc, TextureAddressMode mode, D3D12Filter filter, int count);

	GraphicsDevice* m_pDevice = nullptr;
	ComPtr<ID3DBlob> m_pRootBlob = nullptr;
	ComPtr<ID3D12RootSignature> m_pRootSignature = nullptr;
};

RootSignature.cpp

RootSignature::Create()関数

D3D12_ROOT_SIGNATURE_DESCはルートシグネチャのディスクリプタを定義するための構造体
rootSignatureDesc.pStaticSamplersは静的サンプラーについて説明する構造体へのポインタ(構造体の詳細
rootSignatureDesc.NumStaticSamplersは静的サンプラーの数を指定する
rootSignatureDesc.Flagsはレイアウトのオプションを指定する(列挙の詳細
rootSignatureDesc.pParametersはスロットを説明する構造体を指定(構造体の詳細
rootSignatureDesc.NumParametersはスロットの数を指定する
詳細

D3D12_ROOT_PARAMETERはルートシグネチャディスクリプタのスロットについて説明する構造体
rootParams.ParameterTypeはスロットの種類を指定する(列挙の詳細
rootParams.DescriptorTable.pDescriptorRangesはディスクリプタレンジを指定する(構造体の詳細
rootParams.DescriptorTable.NumDescriptorRangesはテーブルレイアウト内のディスクリプタレンジの数
rootParams.ShaderVisibilityはスロットの内容にアクセスできるシェーダーを指定(列挙の詳細
詳細

D3D12_DESCRIPTOR_RANGEはディスクリプタレンジについて説明する構造体
pRange.NumDescriptorsはレンジ内のディスクリプタの数を指定
pRange.RangeTypeはディスクリプタレンジの種類を指定する(列挙の詳細
pRange.BaseShaderRegisterはレンジ内のベースシェーダーレジスタを指定
pRange.OffsetInDescriptorsFromTableStartはこのパラメータースロットのルート引数値として設定されたディスクリプタテーブルの先頭からのディスクリプタのオフセットを指定
詳細

CreateRange()関数を実行

D3D12_STATIC_SAMPLER_DESCは静的サンプラーについて説明する構造体
pSamplerDesc.AddressUは0~1の範囲外のuテクスチャ座標を解決するために使用するモードを指定する(列挙の詳細
pSamplerDesc.AddressVは0~1の範囲外のvテクスチャ座標を解決するために使用するモードを指定する
pSamplerDesc.AddressWは0~1の範囲外のwテクスチャ座標を解決するために使用するモードを指定する
pSamplerDesc.BorderColorはAddressU、AddressV、またはAddressWにD3D12_TEXTURE_ADDRESS_MODE_BORDERが指定されている場合に使用する罫線の色(列挙の詳細
pSamplerDesc.Filterはテクスチャをサンプリングするとき使用するフィルターオプションを指定(列挙の詳細
pSamplerDesc.MaxLODはアクセスをクランプするミップマップ範囲の上端
pSamplerDesc.MinLODはアクセスをクランプするミップマップ範囲の下端
pSamplerDesc.ComparisonFuncはサンプリングされたデータを既存のサンプリング データと比較する関数を指定(列挙の詳細
pSamplerDesc.ShaderVisibilityはパイプラインシェーダーD3D12_SHADER_VISIBILITYの1つのメンバー) に対するサンプラーの可視性を指定
pSamplerDesc.MaxAnisotropyD3D12_FILTER_ANISOTROPICまたはD3D12_FILTER_COMPARISON_ANISOTROPICがフィルターとして指定されている場合に使用されるクランプ値を指定(有効な値は1~16)
pSamplerDesc.ShaderRegisterはShaderRegister(型がSRV)は2に対応し、RegisterSpaceは3に対応する
詳細

CreateStaticSampler()関数を実行

D3D12SerializeRootSignature()関数はID3D12Device::CreateRootSignatureに渡すことができるルートシグネチャディスクリプタをシリアル化する
▶ 第一引数はルートシグネチャのレイアウトについて説明する構造体へのポインタ(構造体の詳細
▶ 第二引数はルートシグネチャのレイアウトのバージョンを指定(列挙の詳細
▶ 第三引数はシリアル化されたルートシグネチャディスクリプタへのアクセスに使用できるID3DBlobインターフェイスへのポインタを受け取るメモリブロックへのポインタ
▶ 第四引数はシリアライザーのエラーメッセージへのアクセスに使用できるID3DBlobインターフェイスへのポインタを受け取るメモリブロックへのポインタ(エラーがない場合はNULL)
詳細

m_pDevice->GetDevice()->CreateRootSignature()関数はルートシグネチャレイアウトを作成する
▶ 第一引数は単一GPU操作の場合は0に設定し、複数のGPUノードがある場合はルートシグネチャディスクリプタを適用するノード(デバイスの物理アダプター)を識別するビットを設定
▶ 第二引数はシリアル化されたシグネチャディスクリプタのソースデータへのポインタ
▶ 第三引数は第二引数が指すメモリブロックのサイズ
▶ 第四引数はルートシグネチャディスクリプタインターフェイスのGUID
詳細

m_pRootBlob->GetBufferPointer()関数はデータへのポインタを取得する
詳細

m_pRootBlob->GetBufferSize()関数はサイズを取得する
詳細

// RootSignature.cpp

void RootSignature::Create(GraphicsDevice* pGraphicsDevice, const std::vector<RangeType>& rangeTypes) {
	m_pDevice = pGraphicsDevice;

	D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc = {};
	int rangeCount = (int)rangeTypes.size();

	std::vector<D3D12_ROOT_PARAMETER> rootParams(rangeCount);
	std::vector<D3D12_DESCRIPTOR_RANGE> ranges(rangeCount);

	int samplerCount = 0;

	for (int i = 0; i < (int)rangeTypes.size(); ++i) {
		if (rangeTypes[i] == RangeType::SRV) {
			++samplerCount;
		}
	}

	samplerCount = 0;
	bool bSampler = false;
	int cbvCount = 0;
	int uavCount = 0;

	for (int i = 0; i < rangeCount; ++i) {
		switch (rangeTypes[i]) {
		case RangeType::CBV:
			CreateRange(ranges[i], RangeType::CBV, cbvCount);
			rootParams[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
			rootParams[i].DescriptorTable.pDescriptorRanges = &ranges[i];
			rootParams[i].DescriptorTable.NumDescriptorRanges = 1;
			rootParams[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
			++cbvCount;
			break;
		case RangeType::SRV:
			CreateRange(ranges[i], RangeType::SRV, samplerCount);
			rootParams[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
			rootParams[i].DescriptorTable.pDescriptorRanges = &ranges[i];
			rootParams[i].DescriptorTable.NumDescriptorRanges = 1;
			rootParams[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
			++samplerCount;
			bSampler = true;
			break;
		case RangeType::UAV:
			CreateRange(ranges[i], RangeType::UAV, uavCount);
			rootParams[i].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
			rootParams[i].DescriptorTable.pDescriptorRanges = &ranges[i];
			rootParams[i].DescriptorTable.NumDescriptorRanges = 1;
			rootParams[i].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
			++uavCount;
			break;
		default:
			break;
		}
	}

	std::array<D3D12_STATIC_SAMPLER_DESC, 4> pStaticSamplerDescs = {};

	if (bSampler) {
		CreateStaticSampler(pStaticSamplerDescs[0], TextureAddressMode::Wrap, D3D12Filter::Point, 0);
		CreateStaticSampler(pStaticSamplerDescs[1], TextureAddressMode::Clamp, D3D12Filter::Point, 1);
		CreateStaticSampler(pStaticSamplerDescs[2], TextureAddressMode::Wrap, D3D12Filter::Linear, 2);
		CreateStaticSampler(pStaticSamplerDescs[3], TextureAddressMode::Clamp, D3D12Filter::Linear, 3);
	}

	rootSignatureDesc.pStaticSamplers = bSampler ? pStaticSamplerDescs.data() : nullptr;
	rootSignatureDesc.NumStaticSamplers = bSampler ? 4 : 0;
	rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
	rootSignatureDesc.pParameters = rootParams.data();
	rootSignatureDesc.NumParameters = (int)rangeTypes.size();

	ID3DBlob* pErrorBlob = nullptr;
	auto hr = D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1_0, &m_pRootBlob, &pErrorBlob);

	if (FAILED(hr)) {
		assert(0 && "ルートシグネチャ初期化失敗");
	}

	hr = m_pDevice->GetDevice()->CreateRootSignature(0, m_pRootBlob->GetBufferPointer(), m_pRootBlob->GetBufferSize(), IID_PPV_ARGS(&m_pRootSignature));

	if (FAILED(hr)) {
		assert(0 && "ルートシグネチャ作成失敗");
	}
}
RootSignature::CreateRange()関数
// RootSignature.cpp

void RootSignature::CreateRange(D3D12_DESCRIPTOR_RANGE& pRange, RangeType type, int count) {
	switch (type) {
	case RangeType::CBV:
		pRange = {};
		pRange.NumDescriptors = 1;
		pRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
		pRange.BaseShaderRegister = count;
		pRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
		break;
	case RangeType::SRV:
		pRange = {};
		pRange.NumDescriptors = 1;
		pRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
		pRange.BaseShaderRegister = count;
		pRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
		break;
	case RangeType::UAV:
		pRange = {};
		pRange.NumDescriptors = 1;
		pRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV;
		pRange.BaseShaderRegister = count;
		pRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
		break;
	default:
		break;
	}
}
RootSignature::CreateStaticSampler()関数

D3D12_FILTER列挙はテクスチャサンプリング中のフィルターオプションを指定する
詳細

// RootSignature.cpp

void RootSignature::CreateStaticSampler(D3D12_STATIC_SAMPLER_DESC& pSamplerDesc, TextureAddressMode mode, D3D12Filter filter, int count) {
	D3D12_TEXTURE_ADDRESS_MODE addressMode = mode == TextureAddressMode::Wrap ? D3D12_TEXTURE_ADDRESS_MODE_WRAP : D3D12_TEXTURE_ADDRESS_MODE_CLAMP;

	D3D12_FILTER samplingFilter = filter == D3D12Filter::Point ? D3D12_FILTER_MIN_MAG_MIP_POINT : D3D12_FILTER_MIN_MAG_MIP_LINEAR;

	pSamplerDesc = {};
	pSamplerDesc.AddressU = addressMode;
	pSamplerDesc.AddressV = addressMode;
	pSamplerDesc.AddressW = addressMode;
	pSamplerDesc.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
	pSamplerDesc.Filter = samplingFilter;
	pSamplerDesc.MaxLOD = D3D12_FLOAT32_MAX;
	pSamplerDesc.MinLOD = 0.0f;
	pSamplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
	pSamplerDesc.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
	pSamplerDesc.MaxAnisotropy = 16;
	pSamplerDesc.ShaderRegister = count;
}

Pipeline.h

CullMode列挙型クラス

特定の方向に向いている三角形が描画されないように指定する
D3D12_CULL_MODE列挙の詳細

// Pipeline.h

enum class CullMode {
	None = D3D12_CULL_MODE_NONE,
	Front = D3D12_CULL_MODE_FRONT,
	Back = D3D12_CULL_MODE_BACK,
};
BlendMode列挙型クラス

ピクセルの色をどのように合成するかを指定する

// Pipeline.h

enum class BlendMode {
	Add,
	Alpha,
};
InputLayout列挙型クラス

頂点データのセマンティクスを示す

// Pipeline.h

enum class InputLayout {
	POSITION,
	TEXCOORD,
	NORMAL,
	TANGENT,
	COLOR,
	SKININDEX,
	SKINWEIGHT
};
PrimitiveTopologyType列挙型クラス

パイプラインがジオメトリまたはハルシェーダー入力プリミティブを解釈する方法を指定する
D3D12_PRIMITIVE_TOPOLOGY_TYPE列挙の詳細

// Pipeline.h

enum class PrimitiveTopologyType {
	Undefined = D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED,
	Point = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT,
	Line = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE,
	Triangle = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
	Patch = D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH,
};
RootSignatureクラス
// Pipeline.h

class RootSignature;
Pipelineクラス

SetRenderSettings()関数を宣言

Create()関数を宣言

ID3D12PipelineStateは設定されているすべてのシェーダーの状態と、特定の固定関数状態オブジェクトを表すインタフェース
詳細

GetPipeline()関数を定義

PrimitiveTopologyTypeはプリミティブの型とプリミティブデータの順序のD3D12_PRIMITIVE_TOPOLOGY_TYPE型指定された値
詳細

GetTopologyType()関数を定義

SetInputLayout()関数を宣言

SetBlendMode()関数を宣言

// Pipeline.h

class Pipeline {
public:
	void SetRenderSettings(GraphicsDevice* pGraphicsDevice, RootSignature* pRootSignature, const std::vector<InputLayout>& inputLayouts, CullMode cullMode, BlendMode blendMode, PrimitiveTopologyType topologyType);

	void Create(std::vector<ID3DBlob*> pBlobs, const std::vector<DXGI_FORMAT> formats, bool isDepth, bool isDepthMask, int rtvCount, bool isWireFrame);

	ID3D12PipelineState* GetPipeline() { return m_pPipelineState.Get(); }

	PrimitiveTopologyType GetTopologyType() { return m_topologyType; }

private:
	void SetInputLayout(std::vector<D3D12_INPUT_ELEMENT_DESC>& inputElements, const std::vector<InputLayout>& inputLayouts);

	void SetBlendMode(D3D12_RENDER_TARGET_BLEND_DESC& blendDesc, BlendMode blendMode);

	GraphicsDevice* m_pDevice = nullptr;
	RootSignature* m_pRootSignature = nullptr;

	std::vector<InputLayout>	m_inputLayouts;
	CullMode					m_cullMode;
	BlendMode					m_blendMode;
	PrimitiveTopologyType		m_topologyType;

	ComPtr<ID3D12PipelineState> m_pPipelineState = nullptr;
};

Pipeline.cpp

Pipeline::SetRenderSettings()関数

描画の設定を行う

// Pipeline.cpp

void Pipeline::SetRenderSettings(GraphicsDevice* pGraphicsDevice, RootSignature* pRootSignature, const std::vector<InputLayout>& inputLayouts, CullMode cullMode, BlendMode blendMode, PrimitiveTopologyType topologyType) {
	m_pDevice = pGraphicsDevice;
	m_pRootSignature = pRootSignature;
	m_inputLayouts = inputLayouts;
	m_cullMode = cullMode;
	m_blendMode = blendMode;
	m_topologyType = topologyType;
}
Pipeline::Create()関数

作成を行う

D3D12_INPUT_ELEMENT_DESCはグラフィックスパイプラインのinput-assemblerステージの1つの要素について説明する
詳細

D3D12_INPUT_ELEMENT_DESC(頂点レイアウト)

“何バイト目から何バイト目までが何か"という情報を頂点レイアウトといい、このデータを用いてGPUに座標や法線などの"意味"を教える

頂点データには頂点レイアウトが必須である


SetInputLayout()を実行

D3D12_GRAPHICS_PIPELINE_STATE_DESC構造体はグラフィックスパイプラインの状態オブジェクトについて説明する
.VS.pShaderBytecode頂点シェーダーのメモリブロックへのポインタ
.VS.BytecodeLength頂点シェーダーのシェーダーデータのサイズ
.HS.pShaderBytecodeハルシェーダーのメモリブロックへのポインタ
.HS.BytecodeLengthハルシェーダーのシェーダーデータのサイズ
.DS.pShaderBytecodeドメインシェーダーのメモリブロックへのポインタ
.DS.BytecodeLengthドメインシェーダーのシェーダーデータのサイズ
.GS.pShaderBytecodeジオメトリシェーダーのメモリブロックへのポインタ
.GS.BytecodeLengthジオメトリシェーダーのシェーダーデータのサイズ
.PS.pShaderBytecodeピクセルシェーダーのメモリブロックへのポインタ
.PS.BytecodeLengthピクセルシェーダーのシェーダーデータのサイズ
.SampleMaskはブレンド状態のサンプルマスク
.RasterizerState.CullModeは指定した方向に向いている三角形が描画されないことを指定する
.RasterizerState.FillModeはレンダリング時に使用する塗りつぶしモードを指定する
.RasterizerState.DepthClipEnableは距離に基づいてクリッピングを有効にするかどうかを指定する
.BlendState.AlphaToCoverageEnableはピクセルをレンダーターゲットに設定するときに、マルチサンプリング手法としてアルファからカバレッジを使用するかどうかを指定する
.BlendState.IndependentBlendEnableは同時レンダー ターゲットで独立したブレンドを有効にするかどうかを指定する
.BlendState.RenderTarget[0]はレンダーターゲットのブレンド状態を記述するD3D12_RENDER_TARGET_BLEND_DESC構造体の配列
.InputLayout.pInputElementDescsは入力アセンブラーステージのデータ型を記述する
.InputLayout.NumElementsは入力要素の配列内の入力データ型の数
.PrimitiveTopologyTypeはプリミティブの型とプリミティブ データの順序を指定する
.NumRenderTargetsRTVFormatsメンバー内のレンダー ターゲット形式の数
.RTVFormats[i]はレンダーターゲット形式のDXGI_FORMAT型指定された値の配列
.SampleDesc.Countはピクセルあたりのマルチサンプルの数
.pRootSignatureID3D12RootSignatureオブジェクトへのポインタ
詳細

D3D12_RENDER_TARGET_BLEND_DESCはレンダーターゲットのブレンド状態について説明する構造体
blendDesc.RenderTargetWriteMaskは書き込みマスクを指定する
blendDesc.BlendOpはSrcBlend操作とDestBlend操作を組み合わせる方法を定義する
blendDesc.SrcBlendはピクセルシェーダーが出力するRGB値に対して実行する操作を指定する
blendDesc.DestBlendはレンダーターゲットの現在のRGB値に対して実行する操作を指定する
blendDesc.BlendOpAlphaはSrcBlendAlpha操作とDestBlendAlpha操作を組み合わせる方法を定義する
blendDesc.SrcBlendAlphaはピクセルシェーダーが出力するアルファ値に対して実行する操作を指定する
blendDesc.DestBlendAlphaはレンダーターゲットの現在のアルファ値に対して実行する操作を指定する
blendDesc.LogicOpはレンダーターゲットに対して構成する論理操作を指定する
詳細

SetBlendMode()を実行

m_pDevice->GetDevice()->CreateGraphicsPipelineState()はグラフィックスパイプラインの状態オブジェクトを作成する
詳細

// Pipeline.cpp

void Pipeline::Create(std::vector<ID3DBlob*> pBlobs, const std::vector<DXGI_FORMAT> formats, bool bDepth, bool bDepthMask, int rtvCount, bool bWireFrame) {
	std::vector<D3D12_INPUT_ELEMENT_DESC> inputLayouts;
	SetInputLayout(inputLayouts, m_inputLayouts);

	D3D12_GRAPHICS_PIPELINE_STATE_DESC graphicsPipelineState = {};

	graphicsPipelineState.VS.pShaderBytecode = pBlobs[0]->GetBufferPointer();
	graphicsPipelineState.VS.BytecodeLength = pBlobs[0]->GetBufferSize();

	if (pBlobs[1]) {
		graphicsPipelineState.HS.pShaderBytecode = pBlobs[1]->GetBufferPointer();
		graphicsPipelineState.HS.BytecodeLength = pBlobs[1]->GetBufferSize();
	}

	if (pBlobs[2]) {
		graphicsPipelineState.DS.pShaderBytecode = pBlobs[2]->GetBufferPointer();
		graphicsPipelineState.DS.BytecodeLength = pBlobs[2]->GetBufferSize();
	}

	if (pBlobs[3]) {
		graphicsPipelineState.GS.pShaderBytecode = pBlobs[3]->GetBufferPointer();
		graphicsPipelineState.GS.BytecodeLength = pBlobs[3]->GetBufferSize();
	}

	graphicsPipelineState.PS.pShaderBytecode = pBlobs[4]->GetBufferPointer();
	graphicsPipelineState.PS.BytecodeLength = pBlobs[4]->GetBufferSize();

	graphicsPipelineState.SampleMask = D3D12_DEFAULT_SAMPLE_MASK;

	graphicsPipelineState.RasterizerState.CullMode = static_cast<D3D12_CULL_MODE>(m_cullMode);

	if (bWireFrame) {
		graphicsPipelineState.RasterizerState.FillMode = D3D12_FILL_MODE_WIREFRAME;
	}
	else {
		graphicsPipelineState.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID;
	}

	if (bDepth) {
		graphicsPipelineState.RasterizerState.DepthClipEnable = true;
		graphicsPipelineState.DepthStencilState.DepthEnable = true;

		if (bDepthMask) {
			graphicsPipelineState.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
		}
		else {
			graphicsPipelineState.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
		}
		graphicsPipelineState.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
		graphicsPipelineState.DSVFormat = DXGI_FORMAT_D32_FLOAT;
	}
	else {
		graphicsPipelineState.RasterizerState.DepthClipEnable = false;
		graphicsPipelineState.DepthStencilState.DepthEnable = false;
	}

	graphicsPipelineState.BlendState.AlphaToCoverageEnable = false;

	graphicsPipelineState.BlendState.IndependentBlendEnable = false;

	D3D12_RENDER_TARGET_BLEND_DESC blendDesc = {};
	SetBlendMode(blendDesc, m_blendMode);

	graphicsPipelineState.BlendState.RenderTarget[0] = blendDesc;

	graphicsPipelineState.InputLayout.pInputElementDescs = inputLayouts.data();
	graphicsPipelineState.InputLayout.NumElements = (int)m_inputLayouts.size();

	graphicsPipelineState.PrimitiveTopologyType = (pBlobs[3] && pBlobs[4]) ? D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH : static_cast<D3D12_PRIMITIVE_TOPOLOGY_TYPE>(m_topologyType);

	graphicsPipelineState.NumRenderTargets = rtvCount;

	for (int i = 0; i < rtvCount; ++i) {
		graphicsPipelineState.RTVFormats[i] = formats[i];
	}

	graphicsPipelineState.SampleDesc.Count = 1;
	graphicsPipelineState.pRootSignature = m_pRootSignature->GetRootSignature();

	auto hr = m_pDevice->GetDevice()->CreateGraphicsPipelineState(&graphicsPipelineState, IID_PPV_ARGS(&m_pPipelineState));

	if (FAILED(hr)) {
		assert(0 && "パイプラインステートの作成に失敗しました");
		return;
	}
}
Pipeline::SetInputLayout()関数
// Pipeline.cpp

void Pipeline::SetInputLayout(std::vector<D3D12_INPUT_ELEMENT_DESC>& inputElements, const std::vector<InputLayout>& inputLayouts) {
	for (int i = 0; i < (int)inputLayouts.size(); ++i) {
		if (inputLayouts[i] == InputLayout::POSITION) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
		else if (inputLayouts[i] == InputLayout::TEXCOORD) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0, D3D12_APPEND_ALIGNED_ELEMENT,D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA ,0 });
		}
		else if (inputLayouts[i] == InputLayout::NORMAL) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
		else if (inputLayouts[i] == InputLayout::COLOR) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
		else if (inputLayouts[i] == InputLayout::TANGENT) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
		else if (inputLayouts[i] == InputLayout::SKININDEX) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "SKININDEX", 0, DXGI_FORMAT_R16G16B16A16_UINT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
		else if (inputLayouts[i] == InputLayout::SKINWEIGHT) {
			inputElements.emplace_back(D3D12_INPUT_ELEMENT_DESC{ "SKINWEIGHT", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 });
		}
	}
}
Pipeline::SetBlendMode()関数
// Pipeline.cpp

void Pipeline::SetBlendMode(D3D12_RENDER_TARGET_BLEND_DESC& blendDesc, BlendMode blendMode) {
	blendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
	blendDesc.BlendEnable = true;

	switch (blendMode) {
	case BlendMode::Add:
		blendDesc.BlendOp = D3D12_BLEND_OP_ADD;
		blendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
		blendDesc.DestBlend = D3D12_BLEND_ONE;

		blendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
		blendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
		blendDesc.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
		blendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
		break;
	case BlendMode::Alpha:
		blendDesc.BlendOp = D3D12_BLEND_OP_ADD;
		blendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
		blendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;

		blendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
		blendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
		blendDesc.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
		blendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
		break;
	default:
		break;
	}
}

Shader.h

RenderingSetting構造体
// Shader.h

struct RenderingSetting {
	std::vector<InputLayout> InputLayouts;
	std::vector<DXGI_FORMAT> Formats;
	CullMode CullMode = CullMode::Back;
	BlendMode BlendMode = BlendMode::Alpha;
	PrimitiveTopologyType PrimitiveTopologyType = PrimitiveTopologyType::Triangle;
	bool IsDepth = true;
	bool IsDepthMask = true;
	int RTVCount = 1;
	bool IsWireFrame = false;
};
Shaderクラス

作成を行うCreate()関数を宣言

描画を開始するBegin()関数を宣言

メッシュの描画を行うDrawMesh()関数を宣言

シェーダーファイルのロードを行うLoadShaderFile()関数を宣言

// Shader.h

class Shader {
public:
	void Create(GraphicsDevice* pGraphicsDevice, const std::wstring& filePath, const RenderingSetting& renderingSetting, const std::vector<RangeType>& rangeTypes);

	void Begin(int w, int h);

	void DrawMesh(const Mesh& mesh);

private:
	void LoadShaderFile(const std::wstring& filePath);

	GraphicsDevice* m_pDevice = nullptr;

	std::unique_ptr<Pipeline>		m_upPipeline = nullptr;
	std::unique_ptr<RootSignature>	m_upRootSignature = nullptr;

	ID3DBlob* m_pVSBlob = nullptr;		// 頂点シェーダー
	ID3DBlob* m_pHSBlob = nullptr;		// ハルシェーダー
	ID3DBlob* m_pDSBlob = nullptr;		// ドメインシェーダー
	ID3DBlob* m_pGSBlob = nullptr;		// ジオメトリシェーダー
	ID3DBlob* m_pPSBlob = nullptr;		// ピクセルシェーダー
};

Shader.cpp

Shader::Create()関数

LoadShaderFile()を実行

ルートシグネチャを作成するため、m_upRootSignature->Create()を実行

パイプラインの描画の設定を行うため、m_upPipeline->SetRenderSettings()を実行

パイプラインの作成を行うため、m_upPipeline->Create()を実行

// Shader.cpp

void Shader::Create(GraphicsDevice* pGraphicsDevice, const std::wstring& filePath, const RenderingSetting& renderingSetting, const std::vector<RangeType>& rangeTypes) {
	m_pDevice = pGraphicsDevice;

	LoadShaderFile(filePath);

	m_upRootSignature = std::make_unique<RootSignature>();
	m_upRootSignature->Create(pGraphicsDevice, rangeTypes);

	m_upPipeline = std::make_unique<Pipeline>();
	m_upPipeline->SetRenderSettings(pGraphicsDevice, m_upRootSignature.get(), renderingSetting.InputLayouts, renderingSetting.CullMode, renderingSetting.BlendMode, renderingSetting.PrimitiveTopologyType);
	m_upPipeline->Create({ m_pVSBlob ,m_pHSBlob ,m_pDSBlob ,m_pGSBlob ,m_pPSBlob }, renderingSetting.Formats, renderingSetting.IsDepth, renderingSetting.IsDepthMask, renderingSetting.RTVCount, renderingSetting.IsWireFrame);
}
Shader::Begin()関数

m_pDevice->GetCmdList()->SetPipelineState()を実行してパイプライン状態をコマンドリストに割り当てる
詳細

m_pDevice->GetCmdList()->SetGraphicsRootSignature()を実行してグラフィックス ルートシグネチャのレイアウトを設定する
詳細

D3D12_PRIMITIVE_TOPOLOGY_TYPE列挙はパイプラインがジオメトリまたはハルシェーダー入力プリミティブを解釈する方法を指定する
詳細

D3D12_VIEWPORTはビューポートの寸法を指定する
viewport.Widthはビューポートの幅
viewport.Heightはビューポートの高さ
viewport.MinDepthはビューポートの最小深度
viewport.MaxDepthはビューポートの最大深度
詳細

D3D12_RECTはRECT構造体は、左上隅と右下隅の座標によって四角形を定義する
rect.rightは四角形の右下隅のx座標を指定する
rect.bottomは四角形の右下隅のy座標を指定する
詳細1
詳細2

GraphicsDevice::Instance().GetCmdList()->RSSetViewports()を実行してビューポートの配列をパイプラインのラスタライザーステージにバインドする
詳細

GraphicsDevice::Instance().GetCmdList()->RSSetScissorRects()を実行してシザー矩形の配列をラスタライザーステージにバインドする
詳細

// Shader.cpp

void Shader::Begin(int w, int h) {
	m_pDevice->GetCmdList()->SetPipelineState(m_upPipeline->GetPipeline());

	m_pDevice->GetCmdList()->SetGraphicsRootSignature(m_upRootSignature->GetRootSignature());

	D3D12_PRIMITIVE_TOPOLOGY_TYPE topologyType = static_cast<D3D12_PRIMITIVE_TOPOLOGY_TYPE>(m_upPipeline->GetTopologyType());

	switch (topologyType) {
	case D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT:
		m_pDevice->GetCmdList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
		break;
	case D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE:
		m_pDevice->GetCmdList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_LINELIST);
		break;
	case D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE:
		m_pDevice->GetCmdList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
		break;
	case D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH:
		m_pDevice->GetCmdList()->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST);
		break;
	}

	D3D12_VIEWPORT viewport = {};
	D3D12_RECT rect = {};

	viewport.Width = static_cast<float>(w);
	viewport.Height = static_cast<float>(h);
	viewport.MinDepth = 0.0f;
	viewport.MaxDepth = 1.0f;

	rect.right = w;
	rect.bottom = h;

	GraphicsDevice::Instance().GetCmdList()->RSSetViewports(1, &viewport);
	GraphicsDevice::Instance().GetCmdList()->RSSetScissorRects(1, &rect);
}
Shader::DrawMesh()関数

mesh.DrawInstanced()を実行してインスタンス描画を行う

// Shader.cpp

void Shader::DrawMesh(const Mesh& mesh) {
	mesh.DrawInstanced();
}
Shader::LoadShaderFile()関数

ID3DIncludeはシェーダーの内容を開いて読み取るためのインターフェース(DirectXのシェーダーコンパイラがシェーダーコード内で#includeディレクティブを処理するためのメカニズムを提供する)
詳細

D3D_COMPILE_STANDARD_FILE_INCLUDEはシェーダーコンパイラに標準的なファイルベースのインクルードメカニズムを提供し、シェーダーコンパイラはファイルシステム上で直接インクルードファイルを探す

D3DCompileFromFile()は特定のターゲットのバイトコードにHLSLコードをコンパイルする
▶第一引数はシェーダーコードを含むファイルの名前を含む、nullで終わる定数文字列へのポインタ
▶第二引数はシェーダーマクロを定義するD3D_SHADER_MACRO構造体の省略可能な配列
▶第三引数はコンパイラがインクルードファイルの処理に使用するID3DIncludeインターフェイスへの省略可能なポインタ
▶第四引数はシェーダーの実行が開始されるシェーダーエントリポイント関数の名前を含む、null で終わる定数文字列へのポインタ
▶第五引数はコンパイル対象のシェーダーターゲットまたはシェーダー機能のセットを指定する、null で終わる定数文字列へのポインタ
▶第六引数はコンパイラがHLSLコードをコンパイルする方法を指定する
▶第七引数はコンパイラが効果をコンパイルする方法を指定する
▶第八引数はコンパイル済みコードへのアクセスに使用できるID3DBlobインターフェイスへのポインターを受け取る変数へのポインタ
▶第九引数はコンパイラエラーメッセージへのアクセスに使用できるID3DBlobインターフェイスへのポインターを受け取る変数への省略可能なポインタ
詳細

// Shader.cpp

void Shader::LoadShaderFile(const std::wstring& filePath) {
	ID3DInclude* include = D3D_COMPILE_STANDARD_FILE_INCLUDE;
	UINT flag = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
	ID3DBlob* pErrorBlob = nullptr;

	std::wstring format = L".hlsl";
	std::wstring currentPath = L"Asset/Shader/";

	{
		std::wstring fullFilepath = currentPath + filePath + L"_VS" + format;
		auto hr = D3DCompileFromFile(fullFilepath.c_str(), nullptr, include, "main", "vs_5_0", flag, 0, &m_pVSBlob, &pErrorBlob);

		if (FAILED(hr)) {
			assert(0 && "頂点シェーダーのコンパイルに失敗しました");
			return;
		}
	}

	{
		std::wstring fullFilepath = currentPath + filePath + L"_HS" + format;
		auto hr = D3DCompileFromFile(fullFilepath.c_str(), nullptr, include, "main", "hs_5_0", flag, 0, &m_pHSBlob, &pErrorBlob);

		// ハルシェーダーはなくてもいい
	}

	{
		std::wstring fullFilepath = currentPath + filePath + L"_DS" + format;
		auto hr = D3DCompileFromFile(fullFilepath.c_str(), nullptr, include, "main", "ds_5_0", flag, 0, &m_pDSBlob, &pErrorBlob);

		// ドメインシェーダーはなくてもいい
	}

	// ジオメトリシェーダーのコンパイル
	{
		std::wstring fullFilepath = currentPath + filePath + L"_GS" + format;
		auto hr = D3DCompileFromFile(fullFilepath.c_str(), nullptr, include, "main", "gs_5_0", flag, 0, &m_pGSBlob, &pErrorBlob);

		// ジオメトリシェーダーはなくてもいい
	}

	// ピクセルシェーダーのコンパイル
	{
		std::wstring fullFilepath = currentPath + filePath + L"_PS" + format;
		auto hr = D3DCompileFromFile(fullFilepath.c_str(), nullptr, include, "main", "ps_5_0", flag, 0, &m_pPSBlob, &pErrorBlob);

		if (FAILED(hr)) {
			assert(0 && "ピクセルシェーダーのコンパイルに失敗しました");
			return;
		}
	}
}

System.h

System.hにShader.hをインクルード

// System.h

#include "Utility/Utility.h"

#include "../Graphics/RTVHeap/RTVHeap.h"	// GraphicsDevice.hよりも先にインクルード

#include "../Graphics/GraphicsDevice.h"

#include "../Graphics/Mesh/Mesh.h"

#include "../Graphics/Shader/Shader.h"

ポリゴンの描画

GraphicsDevice.h

描画準備を行うPrepare()関数を宣言

// GraphicsDevice.h

class GraphicsDevice {
public:
	// ~ //

	void Prepare();

	// ~ //

GraphicsDevice.cpp

GraphicsDevice::Prepare()関数

m_pSwapChain->GetCurrentBackBufferIndex()を実行して、スワップチェーンの現在のバック バッファーのインデックスを取得する
詳細

リソースとして引数に渡したバッファの扱いを変更する関数SetResourceBarrier()を実行

レンダーターゲットビューのCPU側アドレスを返す関数m_pRTVHeap->GetRTVCPUHandle()を実行

m_pCmdList->OMSetRenderTargets()はレンダーターゲットと深度ステンシルのCPUディスクリプタハンドルを設定する
▶ 第一引数はrtvH配列内のエントリの数
▶ 第二引数はレンダーターゲットディスクリプタのヒープの開始を表すCPUディスクリプタハンドルを記述するD3D12_CPU_DESCRIPTOR_HANDLE構造体の配列を指定
▶ 第三引数について、Falseはハンドルが第一引数のハンドルの配列の最初であることを意味する
▶ 第四引数は深度ステンシルディスクリプタを保持するヒープの開始を表すCPUディスクリプタハンドル
詳細

m_pCmdList->ClearRenderTargetView()はレンダーターゲット内のすべての要素を1つの値に設定する
▶ 第一引数はレンダーターゲットがクリアされるヒープの開始を表すCPUディスクリプタハンドルを記述
▶ 第二引数は塗りつぶす色を表す4成分配列
▶ 第三引数は第四引数のパラメーターが指定する配列内の四角形の数を指定
▶ 第四引数はリソースビューでクリアする四角形のD3D12_RECT構造体の配列を指定し、NULLの場合はリソースビュー全体をクリアする
詳細

// GraphicsDevice.cpp

void GraphicsDevice::Prepare() {
	auto bbIdx = m_pSwapChain->GetCurrentBackBufferIndex();
	SetResourceBarrier(m_pSwapchainBuffers[bbIdx].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);

	auto rtvH = m_pRTVHeap->GetRTVCPUHandle(bbIdx);
	m_pCmdList->OMSetRenderTargets(1, &rtvH, false, nullptr);

	float clearColor[] = { 1.0f,0.0f,1.0f,1.0f }; // 紫色
	m_pCmdList->ClearRenderTargetView(rtvH, clearColor, 0, nullptr);
}
GraphicsDevice::ScreenFlip()関数

m_pSwapChain->GetCurrentBackBufferIndex()はスワップチェーンの現在のバックバッファーのインデックスを取得する 詳細

リソースとして引数に渡したバッファの扱いを変更する関数SetResourceBarrier()を実行

m_pCmdList->Close()はコマンドリストへの記録が完了したことを示す
詳細

m_pCmdQueue->ExecuteCommandLists()は実行するコマンドリストの配列を送信する
詳細

コマンドキューの同期待ちを行う関数WaitForCommandQueue()を実行

m_pCmdAllocator->Reset()はコマンドアロケーターに関連付けられているメモリを再利用することを示す
詳細

m_pCmdList->Reset()は新しいコマンドリストが作成されたかのように、コマンドリストを初期状態に戻す
詳細

m_pSwapChain->Present()はレンダリングされたイメージをユーザーに表示する
詳細

// GraphicsDevice.cpp

void GraphicsDevice::ScreenFlip() {
	/*
	auto bbIdx = m_pSwapChain->GetCurrentBackBufferIndex();
	SetResourceBarrier(m_pSwapchainBuffers[bbIdx].Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);

	auto rtvH = m_pRTVHeap->GetRTVCPUHandle(bbIdx);
	m_pCmdList->OMSetRenderTargets(1, &rtvH, false, nullptr);

	float clearColor[] = { 1.0f, 0.0f, 1.0f, 1.0f };
	m_pCmdList->ClearRenderTargetView(rtvH, clearColor, 0, nullptr);
	*/

	auto bbIdx = m_pSwapChain->GetCurrentBackBufferIndex();

	SetResourceBarrier(m_pSwapchainBuffers[bbIdx].Get(), D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);

	m_pCmdList->Close();
	ID3D12CommandList* cmdlists[] = { m_pCmdList.Get() };
	m_pCmdQueue->ExecuteCommandLists(1, cmdlists);

	WaitForCommandQueue();

	m_pCmdAllocator->Reset();
	m_pCmdList->Reset(m_pCmdAllocator.Get(), nullptr);

	m_pSwapChain->Present(1, 0);
}

Application.cpp

Application::Execute()関数
// Application.cpp

class GraphicsDevice {
public:
	// ~ //

void Application::Execute() {
	
	// ~ //

	Mesh mesh;
	mesh.Create(&GraphicsDevice::Instance());

	RenderingSetting renderingSetting = {};
	renderingSetting.InputLayouts = { InputLayout::POSITION };
	renderingSetting.Formats = { DXGI_FORMAT_R8G8B8A8_UNORM };
	renderingSetting.IsDepth = false;
	renderingSetting.IsDepthMask = false;

	Shader shader;
	shader.Create(&GraphicsDevice::Instance(), L"SimpleShader", renderingSetting, {});

	while (true) {
		if (!m_window.ProcessMessage()) {
			break;
		}

		GraphicsDevice::Instance().Prepare();

		shader.Begin(width, height);

		shader.DrawMesh(mesh);

		GraphicsDevice::Instance().ScreenFlip();
	}
}

結果

クラス図