Box Example의 마지막. BoxApp과 WinMain 함수를 살펴보도록 합시다.

더보기
#pragma once
#include "../Common/D3DApp.h"
#include "../Common/UploadBuffer.h"
#include "../Common/MeshGeometry.h"

using Microsoft::WRL::ComPtr;
using namespace DirectX;
using namespace DirectX::PackedVector;

struct Vertex
{
	XMFLOAT3 Pos;
	XMFLOAT4 Color;
};

struct ObjectConstants
{
	XMFLOAT4X4 WorldViewProj{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
};

class BoxApp : public D3DApp
{
public:
	BoxApp(HINSTANCE hInstance);
	BoxApp(const BoxApp& rhs) = delete;
	BoxApp& operator=(const BoxApp& rhs) = delete;
	virtual ~BoxApp();

	virtual bool Initialize() override;

private:
	virtual void OnResize() override;
	virtual void Update(const GameTimer& gt) override;
	virtual void Draw(const GameTimer& gt) override;

	virtual void OnMouseDown(WPARAM btnState, int x, int y) override;
	virtual void OnMouseUp(WPARAM btnState, int x, int y) override;
	virtual void OnMouseMove(WPARAM btnState, int x, int y) override;

	void BuildDescriptorHeaps();
	void BuildConstantBuffers();
	void BuildRootSignature();
	void BuildShadersAndInputLayout();
	void BuildBoxGeometry();
	void BuildPSO();

private:
	ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
	ComPtr<ID3D12DescriptorHeap> mCbvHeap = nullptr;

	std::unique_ptr<UploadBuffer<ObjectConstants>> mObjectCB = nullptr;
	std::unique_ptr<MeshGeometry> mBoxGeo = nullptr;

	ComPtr<ID3DBlob> mvsByteCode = nullptr;
	ComPtr<ID3DBlob> mpsByteCode = nullptr;

	std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;

	ComPtr<ID3D12PipelineState> mPSO = nullptr;

	XMFLOAT4X4 mWorld{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
	XMFLOAT4X4 mView{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
	XMFLOAT4X4 mProj{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};

	float mTheta = 1.5f * XM_PI;
	float mPhi = XM_PIDIV4;
	float mRadius = 5.f;

	POINT mLastMousePos;
};

헤더 부분을 넘어가기 전에 선언된 Struct들을 보고 가자.

 

struct Vertex
{
	XMFLOAT3 Pos;
	XMFLOAT4 Color;
};

struct ObjectConstants
{
	XMFLOAT4X4 WorldViewProj{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
};

이 예제에서 사용되는 Vertex 정보와 선언된 WorldViewProj이다.

특별한 것은 아니지만 선언 된 것이니 이를 기억해두고 가면 될 것 같다.

 

함수 선언부를 하나하나 살펴보겠습니다.

public:
	BoxApp(HINSTANCE hInstance);
	BoxApp(const BoxApp& rhs) = delete;
	BoxApp& operator=(const BoxApp& rhs) = delete;
	virtual ~BoxApp();

생성자와 소멸자는 D3DApp과 마찬가지로 Singleton을 구현한 형태입니다.

생성자에 virtual 선언이 없지만 구현부를 보면 Initializer로 호출을 해놓은 상태입니다.

 

다음은 기능적인 부분입니다.

virtual bool Initialize() override;

private:
	virtual void OnResize() override;
	virtual void Update(const GameTimer& gt) override;
	virtual void Draw(const GameTimer& gt) override;

	virtual void OnMouseDown(WPARAM btnState, int x, int y) override;
	virtual void OnMouseUp(WPARAM btnState, int x, int y) override;
	virtual void OnMouseMove(WPARAM btnState, int x, int y) override;

D3DApp에서 기본적인 부분만 선언 되어 있거나, 선언되어 있지 않은 함수들의 override가 선언되어 있습니다.

이 형태는 앞으로도 자주 보일 것입니다.

 

다음은 렌더링 부분입니다.

void BuildDescriptorHeaps();
void BuildConstantBuffers();
void BuildRootSignature();
void BuildShadersAndInputLayout();
void BuildBoxGeometry();
void BuildPSO();

DescriptorHeaps, ConstantBuffer 등 렌더링을 하면서 필요한 것들을 선언하고 초기화 하는 함수들입니다.

이 부분은 현재 예시 별로 코드가 분리되어 있지만, 나중에 하나로 합칠 여지가 있다면 합쳐볼 계획입니다.

 

마지막으로 Field들입니다.

private:
	ComPtr<ID3D12RootSignature> mRootSignature = nullptr;
	ComPtr<ID3D12DescriptorHeap> mCbvHeap = nullptr;

	std::unique_ptr<UploadBuffer<ObjectConstants>> mObjectCB = nullptr;
	std::unique_ptr<MeshGeometry> mBoxGeo = nullptr;

	ComPtr<ID3DBlob> mvsByteCode = nullptr;
	ComPtr<ID3DBlob> mpsByteCode = nullptr;

	std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;

	ComPtr<ID3D12PipelineState> mPSO = nullptr;

	XMFLOAT4X4 mWorld{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
	XMFLOAT4X4 mView{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};
	XMFLOAT4X4 mProj{
		1.f, 0.f, 0.f, 0.f,
		0.f, 1.f, 0.f, 0.f,
		0.f, 0.f, 1.f, 0.f,
		0.f, 0.f, 0.f, 1.f
	};

	float mTheta = 1.5f * XM_PI;
	float mPhi = XM_PIDIV4;
	float mRadius = 5.f;

	POINT mLastMousePos;

 

대부분은 이름이나 내용을 보면 이해가 될 것이고, 몇가지만 짚고 넘어가겠습니다.

mObjectCB는 ObjectConstantBuffer의 약자입니다.

mCbvHeap에서 m은 변수 앞에 동일하게 붙는 문자고, Cbv는 Constant Buffer View의 약자입니다.

mvsByteCode, mpsByteCode에서도 앞의 m은 제외하고 봐야 합니다.

vs는 Vertex Shader, ps는 Pixel Shader의 약자입니다.

PSO는 Pipeline State Object의 약자입니다.

 

DirectX 코드를 보면서 가장 불편한 점은 Type을 통해 기능을 명확히 알 수 없다는 점입니다.

결국 무엇인지 알기 위해서는 변수명을 보고 판단해야 하는데, 초보자에게는 약자는 너무 어려운 암호입니다.

이 글을 보고 그래도 대략적인 감은 잡고 가셨으면 좋겠습니다.

 

이제 구현부를 살펴봅시다.

더보기
#include "BoxApp.h"

BoxApp::BoxApp(HINSTANCE hInstance) : D3DApp(hInstance), mLastMousePos(POINT{ 0, 0 })
{
}

BoxApp::~BoxApp()
{
}

bool BoxApp::Initialize()
{
	if (!D3DApp::Initialize())
	{
		return false;
	}

	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	BuildDescriptorHeaps();
	BuildConstantBuffers();
	BuildRootSignature();
	BuildShadersAndInputLayout();
	BuildBoxGeometry();
	BuildPSO();

	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	FlushCommandQueue();
	return true;
}

void BoxApp::OnResize()
{
	D3DApp::OnResize();

	XMMATRIX P = XMMatrixPerspectiveFovLH(XM_PIDIV4, AspectRatio(), 1.0f, 1000.0f);
	XMStoreFloat4x4(&mProj, P);
}

void BoxApp::Update(const GameTimer& gt)
{
	float x = mRadius * sinf(mPhi) * cosf(mTheta);
	float z = mRadius * sinf(mPhi) * sinf(mTheta);
	float y = mRadius * cosf(mPhi);

	XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
	XMVECTOR target = XMVectorZero();
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
	XMStoreFloat4x4(&mView, view);

	XMMATRIX world = XMLoadFloat4x4(&mWorld);
	XMMATRIX proj = XMLoadFloat4x4(&mProj);
	XMMATRIX worldViewProj = world * view * proj;

	ObjectConstants objConstants;
	XMStoreFloat4x4(&objConstants.WorldViewProj, XMMatrixTranspose(worldViewProj));
	mObjectCB->CopyData(0, objConstants);
}

void BoxApp::Draw(const GameTimer& gt)
{
	ThrowIfFailed(mDirectCmdListAlloc->Reset());
	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), mPSO.Get()));

	// Set CommandList
	mCommandList->RSSetViewports(1, &mScreenViewport);
	mCommandList->RSSetScissorRects(1, &mScissorRect);

	// Set CommandList - not problem
	D3D12_RESOURCE_BARRIER BeforeBarrier;
	ZeroMemory(&BeforeBarrier, sizeof(BeforeBarrier));
	BeforeBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	BeforeBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	BeforeBarrier.Transition.pResource = CurrentBackBuffer();
	BeforeBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
	BeforeBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
	BeforeBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	

	mCommandList->ResourceBarrier(1, &BeforeBarrier);

	mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
	mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// Specify the buffers we are going to render to.
	// Set CommandList
	mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());

	ID3D12DescriptorHeap* descriptorHeaps[] = { mCbvHeap.Get() };
	mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

	// Set CommandList
	mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

	// Set CommandList
	mCommandList->IASetVertexBuffers(0, 1, &mBoxGeo->VertexBufferView());
	mCommandList->IASetIndexBuffer(&mBoxGeo->IndexBufferView());
	mCommandList->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Set CommandList
	mCommandList->SetGraphicsRootDescriptorTable(0, mCbvHeap->GetGPUDescriptorHandleForHeapStart());

	mCommandList->DrawIndexedInstanced(mBoxGeo->DrawArgs["box"].IndexCount, 1, 0, 0, 0);

	// Set CommandList - not problem
	D3D12_RESOURCE_BARRIER AfterBarrier;
	ZeroMemory(&AfterBarrier, sizeof(AfterBarrier));
	AfterBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	AfterBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	AfterBarrier.Transition.pResource = CurrentBackBuffer();
	AfterBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
	AfterBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
	AfterBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;

	mCommandList->ResourceBarrier(1, &AfterBarrier);
	ThrowIfFailed(mCommandList->Close());

	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrentBackBuffer = (mCurrentBackBuffer + 1) % SwapChainBufferCount;

	FlushCommandQueue();
}

void BoxApp::OnMouseDown(WPARAM btnState, int x, int y)
{
	mLastMousePos.x = x;
	mLastMousePos.y = y;
	SetCapture(mhMainWnd);
}

void BoxApp::OnMouseUp(WPARAM btnState, int x, int y)
{
	ReleaseCapture();
}

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y)
{
	if ((btnState & MK_LBUTTON) != 0)
	{
		float dx = XMConvertToRadians(0.25f * static_cast<float>(x - mLastMousePos.x));
		float dy = XMConvertToRadians(0.25f * static_cast<float>(y - mLastMousePos.y));

		mTheta += dx;
		mPhi += dy;

		mPhi = Clamp(mPhi, 0.1f, XM_PI - 0.1f);
	}
	else if ((btnState && MK_RBUTTON) != 0)
	{
		float dx = 0.005f * static_cast<float>(x - mLastMousePos.x);
		float dy = 0.005f * static_cast<float>(y - mLastMousePos.y);

		mRadius += dx - dy;

		mRadius = Clamp(mRadius, 3.f, 15.f);
	}
	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

void BoxApp::BuildDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
	cbvHeapDesc.NumDescriptors = 1;
	cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	cbvHeapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&mCbvHeap)));
}

void BoxApp::BuildConstantBuffers()
{
	mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

	UINT objCBByteSize = calcConstantBufferByteSize(sizeof(ObjectConstants));

	D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();
	
	int boxCBufIndex = 0;
	cbAddress += static_cast<UINT64>(boxCBufIndex) * static_cast<UINT64>(objCBByteSize);
	
	D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
	cbvDesc.BufferLocation = cbAddress;
	cbvDesc.SizeInBytes = calcConstantBufferByteSize(sizeof(ObjectConstants));

	md3dDevice->CreateConstantBufferView(&cbvDesc, mCbvHeap->GetCPUDescriptorHandleForHeapStart());
}

void BoxApp::BuildRootSignature()
{
	D3D12_ROOT_PARAMETER slotRootParameter[1];

	D3D12_DESCRIPTOR_RANGE cbvTable;
	cbvTable.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
	cbvTable.NumDescriptors = 1;
	cbvTable.BaseShaderRegister = 0;
	cbvTable.RegisterSpace = 0;
	cbvTable.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
	
	slotRootParameter[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
	slotRootParameter[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
	slotRootParameter[0].DescriptorTable.NumDescriptorRanges = 1;
	slotRootParameter[0].DescriptorTable.pDescriptorRanges = &cbvTable;

	D3D12_ROOT_SIGNATURE_DESC rootSigDesc{ 1, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT };

	ComPtr<ID3DBlob> serializedRootSig = nullptr;
	ComPtr<ID3DBlob> errorBlob = nullptr;
	HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

	if (errorBlob != nullptr)
	{
		::OutputDebugStringA(static_cast<char*>(errorBlob->GetBufferPointer()));
	}
	ThrowIfFailed(hr);

	ThrowIfFailed(md3dDevice->CreateRootSignature(
		0, 
		serializedRootSig->GetBufferPointer(), 
		serializedRootSig->GetBufferSize(), 
		IID_PPV_ARGS(&mRootSignature)));

}

void BoxApp::BuildShadersAndInputLayout()
{
	HRESULT hr = S_OK;
	
	mvsByteCode = CompileShader(L"Shaders\\Ch06\\color.hlsl", nullptr, "VS", "vs_5_0");
	mpsByteCode = CompileShader(L"Shaders\\Ch06\\color.hlsl", nullptr, "PS", "ps_5_0");

	mInputLayout =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
		{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
	};
}

void BoxApp::BuildBoxGeometry()
{
	std::array<Vertex, 8> vertices
	{
		Vertex({XMFLOAT3(-1.f, -1.f, -1.f), XMFLOAT4(Colors::White)}), 
		Vertex({XMFLOAT3(-1.f, 1.f, -1.f), XMFLOAT4(Colors::Black)}),
		Vertex({XMFLOAT3(1.f, 1.f, -1.f), XMFLOAT4(Colors::Red)}),
		Vertex({XMFLOAT3(1.f, -1.f, -1.f), XMFLOAT4(Colors::Green)}),
		Vertex({XMFLOAT3(-1.f, -1.f, 1.f), XMFLOAT4(Colors::Blue)}),
		Vertex({XMFLOAT3(-1.f, 1.f, 1.f), XMFLOAT4(Colors::Yellow)}),
		Vertex({XMFLOAT3(1.f, 1.f, 1.f), XMFLOAT4(Colors::Cyan)}),
		Vertex({XMFLOAT3(1.f, -1.f, 1.f), XMFLOAT4(Colors::Magenta)}),
	};

	std::array<std::uint16_t, 36> indices =
	{
		0, 1, 2, 
		0, 2, 3, 
		
		4, 6, 5, 
		4, 7, 6, 
		
		4, 5, 1, 
		4, 1, 0, 
		
		3, 2, 6, 
		3, 6, 7, 
		
		1, 5, 6, 
		1, 6, 2,

		4, 0, 3, 
		4, 3, 7
	};

	const UINT vbByteSize = static_cast<UINT>(vertices.size()) * sizeof(Vertex);
	const UINT ibByteSize = static_cast<UINT>(indices.size()) * sizeof(std::uint16_t);

	mBoxGeo = std::make_unique<MeshGeometry>();
	mBoxGeo->Name = "boxGeo";

	ThrowIfFailed(D3DCreateBlob(vbByteSize, &mBoxGeo->VertexBufferCPU));
	CopyMemory(mBoxGeo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

	ThrowIfFailed(D3DCreateBlob(ibByteSize, &mBoxGeo->IndexBufferCPU));
	CopyMemory(mBoxGeo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

	mBoxGeo->VertexBufferGPU = CreateDefaultbuffer(md3dDevice.Get(), mCommandList.Get(), vertices.data(), vbByteSize, mBoxGeo->VertexBufferUploader);
	mBoxGeo->IndexBufferGPU = CreateDefaultbuffer(md3dDevice.Get(), mCommandList.Get(), indices.data(), ibByteSize, mBoxGeo->IndexBufferUploader);

	mBoxGeo->VertexByteStride = sizeof(Vertex);
	mBoxGeo->VertexBufferByteSize = vbByteSize;
	mBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT;
	mBoxGeo->IndexBufferByteSize = ibByteSize;

	SubmeshGeometry submesh;
	submesh.IndexCount = static_cast<UINT>(indices.size());
	submesh.StartIndexLocation = 0;
	submesh.BaseVertexLocation = 0;

	mBoxGeo->DrawArgs["box"] = submesh;
}

void BoxApp::BuildPSO()
{
	D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
	ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
	psoDesc.InputLayout = D3D12_INPUT_LAYOUT_DESC{ mInputLayout.data(), static_cast<UINT>(mInputLayout.size()) };
	psoDesc.pRootSignature = mRootSignature.Get();
	psoDesc.VS =
	{
		reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()),
		mvsByteCode->GetBufferSize()
	};
	psoDesc.PS =
	{
		reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()),
		mpsByteCode->GetBufferSize()
	};
	
	D3D12_RASTERIZER_DESC Rasterizer;
	Rasterizer.FillMode = D3D12_FILL_MODE_SOLID;
	Rasterizer.CullMode = D3D12_CULL_MODE_BACK;
	Rasterizer.FrontCounterClockwise = FALSE;
	Rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
	Rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
	Rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
	Rasterizer.DepthClipEnable = TRUE;
	Rasterizer.MultisampleEnable = FALSE;
	Rasterizer.AntialiasedLineEnable = FALSE;
	Rasterizer.ForcedSampleCount = 0;
	Rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
	
	D3D12_BLEND_DESC Blend;
	Blend.AlphaToCoverageEnable = FALSE;
	Blend.IndependentBlendEnable = FALSE;
	const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc
	{
		FALSE, FALSE, 
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, 
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, 
		D3D12_LOGIC_OP_NOOP, 
		D3D12_COLOR_WRITE_ENABLE_ALL,
	};
	for (auto& iter : Blend.RenderTarget)
	{
		iter = defaultRenderTargetBlendDesc;
	}

	D3D12_DEPTH_STENCIL_DESC DepthStencil;
	DepthStencil.DepthEnable = TRUE;
	DepthStencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
	DepthStencil.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
	DepthStencil.StencilEnable = FALSE;
	DepthStencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
	DepthStencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
	const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = 
	{
		D3D12_STENCIL_OP_KEEP, 
		D3D12_STENCIL_OP_KEEP,
		D3D12_STENCIL_OP_KEEP,
		D3D12_COMPARISON_FUNC_ALWAYS
	};
	DepthStencil.FrontFace = defaultStencilOp;
	DepthStencil.BackFace = defaultStencilOp;

	psoDesc.RasterizerState = Rasterizer;
	psoDesc.BlendState = Blend;
	psoDesc.DepthStencilState = DepthStencil;
	psoDesc.SampleMask = UINT_MAX;
	psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	psoDesc.NumRenderTargets = 1;
	psoDesc.RTVFormats[0] = mBackBufferFormat;
	psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	psoDesc.DSVFormat = mDepthStencilFormat;
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));
}

 

생성자와 소멸자 부분은 별 내용이 없으므로 설명을 생략하겠습니다.

BoxApp::BoxApp(HINSTANCE hInstance) : D3DApp(hInstance), mLastMousePos(POINT{ 0, 0 })
{
}

BoxApp::~BoxApp()
{
}

 

다음은 Initializer입니다.

bool BoxApp::Initialize()
{
	if (!D3DApp::Initialize())
	{
		return false;
	}

	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr));
	BuildDescriptorHeaps();
	BuildConstantBuffers();
	BuildRootSignature();
	BuildShadersAndInputLayout();
	BuildBoxGeometry();
	BuildPSO();

	ThrowIfFailed(mCommandList->Close());
	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	FlushCommandQueue();
	return true;
}

상위 class인 D3DApp의 Initialize()가 완료가 되었다면, Box를 렌더링 할 함수들을 호출합니다.

이에 앞서 CommandList를 초기화 하고, Private로 선언된 모든 Build 함수들을 호출합니다.

Build가 완료가 되면 CommandList를 닫아주고,

CommandList 안의 내용을 복사하여 CommandQueue에 넣어주고 이를 실행합니다.

내용이 실행이 된 이후에는 CommandQueue를 초기화 하고,

이 과정이 마무리 되면 모든 기능이 정상 작동 되었기에 true를 반환합니다.

 

뒷 함수들에 앞서 그럼 Build 함수들을 먼저 살펴보겠습니다.

가장 먼저 볼 것은 BuildDescriptorHeaps입니다.

void BoxApp::BuildDescriptorHeaps()
{
	D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
	cbvHeapDesc.NumDescriptors = 1;
	cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
	cbvHeapDesc.NodeMask = 0;
	ThrowIfFailed(md3dDevice->CreateDescriptorHeap(&cbvHeapDesc, IID_PPV_ARGS(&mCbvHeap)));
}

앞에 거창하게 소개 했지만 사실 큰 내용은 없습니다.

DescriptorHeaps에서 사용하는 구조체인 D3D12_DESCRIPTOR_HEAP_DESC 안에 내용을 채워 넣고 생성해주면 됩니다.

 

다음은 BuildConstantBuffers입니다.

void BoxApp::BuildConstantBuffers()
{
	mObjectCB = std::make_unique<UploadBuffer<ObjectConstants>>(md3dDevice.Get(), 1, true);

	UINT objCBByteSize = calcConstantBufferByteSize(sizeof(ObjectConstants));

	D3D12_GPU_VIRTUAL_ADDRESS cbAddress = mObjectCB->Resource()->GetGPUVirtualAddress();
	
	int boxCBufIndex = 0;
	cbAddress += static_cast<UINT64>(boxCBufIndex) * static_cast<UINT64>(objCBByteSize);
	
	D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
	cbvDesc.BufferLocation = cbAddress;
	cbvDesc.SizeInBytes = calcConstantBufferByteSize(sizeof(ObjectConstants));

	md3dDevice->CreateConstantBufferView(&cbvDesc, mCbvHeap->GetCPUDescriptorHandleForHeapStart());
}

UploadBuffer의 사이즈와 ConstantBuffer의 ByteSize를 연산해 GPUVirtualAddress에 그 크기만큼 공간을 잡아줍니다.

그리고 ConstantBufferView의 시작 위치와 크기를 위에서 연산한 것들로 넣어 줘 ConstantBufferView를 생성합니다.

 

다음은 BuildRootSignature입니다. 이번건 길이가 좀 됩니다.

void BoxApp::BuildRootSignature()
{
	D3D12_ROOT_PARAMETER slotRootParameter[1];

	D3D12_DESCRIPTOR_RANGE cbvTable;
	cbvTable.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
	cbvTable.NumDescriptors = 1;
	cbvTable.BaseShaderRegister = 0;
	cbvTable.RegisterSpace = 0;
	cbvTable.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
	
	slotRootParameter[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
	slotRootParameter[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL;
	slotRootParameter[0].DescriptorTable.NumDescriptorRanges = 1;
	slotRootParameter[0].DescriptorTable.pDescriptorRanges = &cbvTable;

	D3D12_ROOT_SIGNATURE_DESC rootSigDesc{ 1, slotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT };

	ComPtr<ID3DBlob> serializedRootSig = nullptr;
	ComPtr<ID3DBlob> errorBlob = nullptr;
	HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());

	if (errorBlob != nullptr)
	{
		::OutputDebugStringA(static_cast<char*>(errorBlob->GetBufferPointer()));
	}
	ThrowIfFailed(hr);

	ThrowIfFailed(md3dDevice->CreateRootSignature(
		0, 
		serializedRootSig->GetBufferPointer(), 
		serializedRootSig->GetBufferSize(), 
		IID_PPV_ARGS(&mRootSignature)));

}

이 과정은 Shader를 Application에 알맞게 작성 되었는지 확인하고 허가 받는 과정입니다.

때문에 반드시 Shader와 호환이 되어야 합니다.

만약 Shader Program이 달라진다면, Root Signature도 달라지게 됩니다.

 

자세한 내용은 아래 링크를 참조하면 될 것 같습니다.

ssinyoung.tistory.com/41

 

DirectX 12 루트 서명(Root Signature)과 파이프라인 상태(Pipeline State)

[ 루트 서명(Root Signature) ] 루트 서명(Root Signature)란 어떤 리소스(데이터)들이 그래픽스 파이프라인의 셰이더에 연결되는 가를 정의하는 것이다. 즉, 단어 그대로 작성한 셰이더를 응용 프로그램��

ssinyoung.tistory.com

docs.microsoft.com/ko-kr/windows/win32/direct3d12/root-signatures

 

루트 서명 - Win32 apps

루트 서명은 그래픽 파이프라인에 바인딩된 리소스 종류를 정의합니다.

docs.microsoft.com

다음은 BuildShadersAndInputLayout입니다.

void BoxApp::BuildShadersAndInputLayout()
{
	HRESULT hr = S_OK;
	
	mvsByteCode = CompileShader(L"Shaders\\Ch06\\color.hlsl", nullptr, "VS", "vs_5_0");
	mpsByteCode = CompileShader(L"Shaders\\Ch06\\color.hlsl", nullptr, "PS", "ps_5_0");

	mInputLayout =
	{
		{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
		{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
	};
}

이는 저장되어 있는 Shader 코드를 읽어오는 함수이다.

나중에 팀 내에서 사용하는 방식에 따라 달라지겠지만, 적어도 이 프로젝트에서는 그 형태가 변할 이유는 없다.

 

이 함수는 저장된 Vertex와 Pixel들을 이용해 Box Shader를 만드는 BuildBoxGeometry이다.

void BoxApp::BuildBoxGeometry()
{
	std::array<Vertex, 8> vertices
	{
		Vertex({XMFLOAT3(-1.f, -1.f, -1.f), XMFLOAT4(Colors::White)}), 
		Vertex({XMFLOAT3(-1.f, 1.f, -1.f), XMFLOAT4(Colors::Black)}),
		Vertex({XMFLOAT3(1.f, 1.f, -1.f), XMFLOAT4(Colors::Red)}),
		Vertex({XMFLOAT3(1.f, -1.f, -1.f), XMFLOAT4(Colors::Green)}),
		Vertex({XMFLOAT3(-1.f, -1.f, 1.f), XMFLOAT4(Colors::Blue)}),
		Vertex({XMFLOAT3(-1.f, 1.f, 1.f), XMFLOAT4(Colors::Yellow)}),
		Vertex({XMFLOAT3(1.f, 1.f, 1.f), XMFLOAT4(Colors::Cyan)}),
		Vertex({XMFLOAT3(1.f, -1.f, 1.f), XMFLOAT4(Colors::Magenta)}),
	};

	std::array<std::uint16_t, 36> indices =
	{
		0, 1, 2, 
		0, 2, 3, 
		
		4, 6, 5, 
		4, 7, 6, 
		
		4, 5, 1, 
		4, 1, 0, 
		
		3, 2, 6, 
		3, 6, 7, 
		
		1, 5, 6, 
		1, 6, 2,

		4, 0, 3, 
		4, 3, 7
	};

	const UINT vbByteSize = static_cast<UINT>(vertices.size()) * sizeof(Vertex);
	const UINT ibByteSize = static_cast<UINT>(indices.size()) * sizeof(std::uint16_t);

	mBoxGeo = std::make_unique<MeshGeometry>();
	mBoxGeo->Name = "boxGeo";

	ThrowIfFailed(D3DCreateBlob(vbByteSize, &mBoxGeo->VertexBufferCPU));
	CopyMemory(mBoxGeo->VertexBufferCPU->GetBufferPointer(), vertices.data(), vbByteSize);

	ThrowIfFailed(D3DCreateBlob(ibByteSize, &mBoxGeo->IndexBufferCPU));
	CopyMemory(mBoxGeo->IndexBufferCPU->GetBufferPointer(), indices.data(), ibByteSize);

	mBoxGeo->VertexBufferGPU = CreateDefaultbuffer(md3dDevice.Get(), mCommandList.Get(), vertices.data(), vbByteSize, mBoxGeo->VertexBufferUploader);
	mBoxGeo->IndexBufferGPU = CreateDefaultbuffer(md3dDevice.Get(), mCommandList.Get(), indices.data(), ibByteSize, mBoxGeo->IndexBufferUploader);

	mBoxGeo->VertexByteStride = sizeof(Vertex);
	mBoxGeo->VertexBufferByteSize = vbByteSize;
	mBoxGeo->IndexFormat = DXGI_FORMAT_R16_UINT;
	mBoxGeo->IndexBufferByteSize = ibByteSize;

	SubmeshGeometry submesh;
	submesh.IndexCount = static_cast<UINT>(indices.size());
	submesh.StartIndexLocation = 0;
	submesh.BaseVertexLocation = 0;

	mBoxGeo->DrawArgs["box"] = submesh;
}

Model의 Vertices와 Indices를 자성해주고 이를 각각의 Buffer에 쌓아준다.

 

마지막으로 볼 Build 함수는 BuildPSO이다.

void BoxApp::BuildPSO()
{
	D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
	ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
	psoDesc.InputLayout = D3D12_INPUT_LAYOUT_DESC{ mInputLayout.data(), static_cast<UINT>(mInputLayout.size()) };
	psoDesc.pRootSignature = mRootSignature.Get();
	psoDesc.VS =
	{
		reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()),
		mvsByteCode->GetBufferSize()
	};
	psoDesc.PS =
	{
		reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()),
		mpsByteCode->GetBufferSize()
	};
	
	D3D12_RASTERIZER_DESC Rasterizer;
	Rasterizer.FillMode = D3D12_FILL_MODE_SOLID;
	Rasterizer.CullMode = D3D12_CULL_MODE_BACK;
	Rasterizer.FrontCounterClockwise = FALSE;
	Rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
	Rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
	Rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
	Rasterizer.DepthClipEnable = TRUE;
	Rasterizer.MultisampleEnable = FALSE;
	Rasterizer.AntialiasedLineEnable = FALSE;
	Rasterizer.ForcedSampleCount = 0;
	Rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
	
	D3D12_BLEND_DESC Blend;
	Blend.AlphaToCoverageEnable = FALSE;
	Blend.IndependentBlendEnable = FALSE;
	const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc
	{
		FALSE, FALSE, 
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, 
		D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD, 
		D3D12_LOGIC_OP_NOOP, 
		D3D12_COLOR_WRITE_ENABLE_ALL,
	};
	for (auto& iter : Blend.RenderTarget)
	{
		iter = defaultRenderTargetBlendDesc;
	}

	D3D12_DEPTH_STENCIL_DESC DepthStencil;
	DepthStencil.DepthEnable = TRUE;
	DepthStencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
	DepthStencil.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
	DepthStencil.StencilEnable = FALSE;
	DepthStencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
	DepthStencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
	const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = 
	{
		D3D12_STENCIL_OP_KEEP, 
		D3D12_STENCIL_OP_KEEP,
		D3D12_STENCIL_OP_KEEP,
		D3D12_COMPARISON_FUNC_ALWAYS
	};
	DepthStencil.FrontFace = defaultStencilOp;
	DepthStencil.BackFace = defaultStencilOp;

	psoDesc.RasterizerState = Rasterizer;
	psoDesc.BlendState = Blend;
	psoDesc.DepthStencilState = DepthStencil;
	psoDesc.SampleMask = UINT_MAX;
	psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	psoDesc.NumRenderTargets = 1;
	psoDesc.RTVFormats[0] = mBackBufferFormat;
	psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
	psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
	psoDesc.DSVFormat = mDepthStencilFormat;
	ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));
}

굉장히 길지만 내용은 별거 없다.

1. Pipeline State Object를 생성하고 정보를 채워넣어준다.

2. 안에서 사용할 Rasterizer, DepthStencil, Blend 객체들을 생성해서 채워넣어준다.

외부 지원 함수를 사용하지 않아서 이렇게 긴 것일 뿐이다.

 

기나긴 Build 함수들을 뒤로하고 다시 Override 되는 기능 함수들을 살펴보자.

OnResize 함수이다.

void BoxApp::OnResize()
{
	D3DApp::OnResize();

	XMMATRIX P = XMMatrixPerspectiveFovLH(XM_PIDIV4, AspectRatio(), 1.0f, 1000.0f);
	XMStoreFloat4x4(&mProj, P);
}

기본적인 화면 크기 변경에 따른 기능들은 상위 클래스에서 해결해준다.

여기서는 Fov(File of View)LH(Left-Handed)를 조정하여 Model의 종횡비를 조절해준다.

 

지금부터는 상위 클래스에서 비어 있었던 함수들이다.

가장 먼저 볼 것은 Update이다.

void BoxApp::Update(const GameTimer& gt)
{
	float x = mRadius * sinf(mPhi) * cosf(mTheta);
	float z = mRadius * sinf(mPhi) * sinf(mTheta);
	float y = mRadius * cosf(mPhi);

	XMVECTOR pos = XMVectorSet(x, y, z, 1.0f);
	XMVECTOR target = XMVectorZero();
	XMVECTOR up = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	XMMATRIX view = XMMatrixLookAtLH(pos, target, up);
	XMStoreFloat4x4(&mView, view);

	XMMATRIX world = XMLoadFloat4x4(&mWorld);
	XMMATRIX proj = XMLoadFloat4x4(&mProj);
	XMMATRIX worldViewProj = world * view * proj;

	ObjectConstants objConstants;
	XMStoreFloat4x4(&objConstants.WorldViewProj, XMMatrixTranspose(worldViewProj));
	mObjectCB->CopyData(0, objConstants);
}

매 프레임마다 호출되는 Update에서는 현재까지의 입력값을 통해 Model의 각 Vertex들이 갖는 좌표 값을 연산해낸다.

좌표값은 mPhi나 mTheta, mRadius에 저장되어 있으며, 값이 변하는 시기는 OnMouseMove이다.

void BoxApp::OnMouseMove(WPARAM btnState, int x, int y)
{
	if ((btnState & MK_LBUTTON) != 0)
	{
		float dx = XMConvertToRadians(0.25f * static_cast<float>(x - mLastMousePos.x));
		float dy = XMConvertToRadians(0.25f * static_cast<float>(y - mLastMousePos.y));

		mTheta += dx;
		mPhi += dy;

		mPhi = Clamp(mPhi, 0.1f, XM_PI - 0.1f);
	}
	else if ((btnState && MK_RBUTTON) != 0)
	{
		float dx = 0.005f * static_cast<float>(x - mLastMousePos.x);
		float dy = 0.005f * static_cast<float>(y - mLastMousePos.y);

		mRadius += dx - dy;

		mRadius = Clamp(mRadius, 3.f, 15.f);
	}
	mLastMousePos.x = x;
	mLastMousePos.y = y;
}

좌클릭과 우클릭 시 좌표 값에 따라 회전이나 크기 값에 영향을 준다.

 

더불어 다른 Mouse Cursor 입력과 관련된 OnMouseDown, OnMouseUP 함수는 다음과 같다.

void BoxApp::OnMouseDown(WPARAM btnState, int x, int y)
{
	mLastMousePos.x = x;
	mLastMousePos.y = y;
	SetCapture(mhMainWnd);
}

void BoxApp::OnMouseUp(WPARAM btnState, int x, int y)
{
	ReleaseCapture();
}

 

 

마지막 남은 Draw 함수를 살펴보자. 이 함수는 조금 어렵다.

void BoxApp::Draw(const GameTimer& gt)
{
	ThrowIfFailed(mDirectCmdListAlloc->Reset());
	ThrowIfFailed(mCommandList->Reset(mDirectCmdListAlloc.Get(), mPSO.Get()));

	// Set CommandList
	mCommandList->RSSetViewports(1, &mScreenViewport);
	mCommandList->RSSetScissorRects(1, &mScissorRect);

	// Set CommandList - not problem
	D3D12_RESOURCE_BARRIER BeforeBarrier;
	ZeroMemory(&BeforeBarrier, sizeof(BeforeBarrier));
	BeforeBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	BeforeBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	BeforeBarrier.Transition.pResource = CurrentBackBuffer();
	BeforeBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
	BeforeBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
	BeforeBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
	

	mCommandList->ResourceBarrier(1, &BeforeBarrier);

	mCommandList->ClearRenderTargetView(CurrentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
	mCommandList->ClearDepthStencilView(DepthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	// Specify the buffers we are going to render to.
	// Set CommandList
	mCommandList->OMSetRenderTargets(1, &CurrentBackBufferView(), true, &DepthStencilView());

	ID3D12DescriptorHeap* descriptorHeaps[] = { mCbvHeap.Get() };
	mCommandList->SetDescriptorHeaps(_countof(descriptorHeaps), descriptorHeaps);

	// Set CommandList
	mCommandList->SetGraphicsRootSignature(mRootSignature.Get());

	// Set CommandList
	mCommandList->IASetVertexBuffers(0, 1, &mBoxGeo->VertexBufferView());
	mCommandList->IASetIndexBuffer(&mBoxGeo->IndexBufferView());
	mCommandList->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	// Set CommandList
	mCommandList->SetGraphicsRootDescriptorTable(0, mCbvHeap->GetGPUDescriptorHandleForHeapStart());

	mCommandList->DrawIndexedInstanced(mBoxGeo->DrawArgs["box"].IndexCount, 1, 0, 0, 0);

	// Set CommandList - not problem
	D3D12_RESOURCE_BARRIER AfterBarrier;
	ZeroMemory(&AfterBarrier, sizeof(AfterBarrier));
	AfterBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	AfterBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
	AfterBarrier.Transition.pResource = CurrentBackBuffer();
	AfterBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
	AfterBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
	AfterBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;

	mCommandList->ResourceBarrier(1, &AfterBarrier);
	ThrowIfFailed(mCommandList->Close());

	ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
	mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

	ThrowIfFailed(mSwapChain->Present(0, 0));
	mCurrentBackBuffer = (mCurrentBackBuffer + 1) % SwapChainBufferCount;

	FlushCommandQueue();
}

그리기 작업도 결국 명령이기에 CommandList를 거친다.

그렇기에 CommandListAllocator와 CommandList를 초기화해주고, 필요한 설정을 해준다.

그리고 명령을 입력하기 전 후로 Barrier를 생성해서 추가해 명령의 구분선을 그어준다.

 

실질적인 그리기 명령은 Render Target View와 Depth Stencil View를 초기화 하고, RenderTarget을 지정해준다.

다음 Descriptor Heaps에 Constant Buffer View를 가져다가 설정해주고, RootSignature를 설정한다.

이후 VertexBuffer, IndexBuffer를 채워넣어주고 Privitive Topology 생성, RootDescriptorTable 설정을 한 뒤

그리기 함수를 호출한다.

 

마무리 Barrier가 채워지면 CommandQueue를 실행하고 이를 비워준다.

여기서 우리가 기억해야 할 것은 3가지이다.

1. 명령 전에는 항상 Command List를 초기화 해주고,

명령 후에는 이를 복사한 뒤 CommandQueue에 넣고 실행.

실행 후에는 CommandQueue도 Flush.

2. 명령 전후에는 항상 Barrier를 생성.

3. DirectX12의 렌더링 방식에 따른 그리기 함수 호출.

 

이로써 DirectX와 Window를 생성해주는 모든 코드를 살펴보았다.

마무리로 Main함수를 살펴보자.

// DirectX12ExampleCode.cpp : Defines the entry point for the application.
//

#include "framework.h"
#include "DirectX12ExampleCode.h"
#include "Example/BoxApp.h"

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: Place code here.
    D3DApp* App = new BoxApp(hInstance);

    // Perform application initialization:
    if (!App->Initialize())
    {
        return FALSE;
    }

    // Main message loop:
    try
    {
        return App->run();
    }
    catch (DXException& e)
    {
        MessageBox(nullptr, e.ToString().c_str(), L"HR Failed", MB_OK);
        return 0;
    }
}

 

주석으로 처리된 원래 함수들은 삭제했다.

전체적인 선언이나 구성은 무시해도 좋다. 어차피 Windows 관련 배경지식이 있어야 한다.

그저 Try-Catch로 run 함수에서의 에러를 잡아준다는 것만 보고 가자.

 

이로써 기나긴 BoxApp 예시를 모두 살펴보았습니다.

다음에는 외부 코드를 적절히 프로젝트에 적용해서 코드를 간략화 할 것입니다.

이 과정이 끝나면 책에서의 Chapter6를 보고 정리한 뒤, 다음 Chapter로 넘어갈 계획입니다.

'내용정리 > DirectX12' 카테고리의 다른 글

20.09.18 개발일지  (0) 2020.09.18
10. Box Example 2 - D3DApp  (0) 2020.09.08
10. Box Example 1  (0) 2020.09.04
20.08.25 개발일지  (0) 2020.08.25
20.08.21 개발일지  (0) 2020.08.21

+ Recent posts