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.h
とMesh.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.Format
はDXGI_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.hlsl
とSimpleShader_PS.hlsl
を追加
Graphics
フォルダにShader
フォルダを追加し、Shader.h
とShader.cpp
を追加
Shader
フォルダにRootSignature
フォルダを追加し、RootSignature.h
とRootSignature.cpp
を追加
Shader
フォルダにPipeline
フォルダを追加し、Pipeline.h
とPipeline.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.MaxAnisotropy
はD3D12_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
はプリミティブの型とプリミティブ データの順序を指定する
▶ .NumRenderTargets
はRTVFormats
メンバー内のレンダー ターゲット形式の数
▶ .RTVFormats[i]
はレンダーターゲット形式のDXGI_FORMAT
型指定された値の配列
▶ .SampleDesc.Count
はピクセルあたりのマルチサンプルの数
▶ .pRootSignature
はID3D12RootSignature
オブジェクトへのポインタ
詳細
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();
}
}